Dans le code plpgsql, SELECT
sans cible déclenche une erreur. Mais vous ne le faites manifestement pas voulez SELECT INTO
, vous voulez juste définir le statut de FOUND
. Vous utiliseriez PERFORM
pour ça.
- SELECT lève une exception dans la fonction PL/pgSQL
Mieux, encore , utilisez IF EXISTS ...
. Considérez cette réécriture de votre fonction :
CREATE OR REPLACE FUNCTION "insertarNuevoArticulo"( nombrearticulo text, descripcion text, idtipo int, idfamilia bigint, artstock int, minstock int, maxstock int, idmarca bigint, precio real, marcastock int)
RETURNS boolean
LANGUAGE plpgsql AS
$func$
DECLARE
_id_articulo "Articulo"."idArticulo"%TYPE;
BEGIN
SELECT a."idArticulo" INTO _id_articulo
FROM "Articulo" a
WHERE a."Nombre" = $1 AND a."idTipo" = $3 AND a."idFamilia" = $4;
IF NOT FOUND THEN
INSERT INTO "Articulo"("Nombre", "Descripcion", "idTipo", "idFamilia", "Stock", "MinStock", "MaxStock")
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING "Articulo"."idArticulo" INTO _id_articulo;
END IF;
IF EXISTS (SELECT FROM "ArticuloMarca" a
WHERE a."idArticulo" = _id_articulo AND a."idMarca" = $8) THEN
RETURN false;
ELSE
INSERT INTO "ArticuloMarca"("idArticulo", "idMarca", "PrecioReferencial", "Stock")
VALUES (_id_articulo, $8, $9, $10);
RETURN true;
END IF;
END
$func$;
À propos de EXISTS
:
- PL/pgSQL vérifie si une ligne existe
L'autre point majeur :
- Utilisez le
RETURNING
clause duINSERT
au lieu d'unSELECT
supplémentaire .
Postgres 9.5+
Dans Postgres 9.5 ou version ultérieure, utilisez INSERT ... ON CONFLICT DO NOTHING
(alias "UPSERT") à la place.
Vous auriez UNIQUE
contraintes sur "Articulo"("Nombre", "idTipo", "idFamilia")
et "ArticuloMarca"("idArticulo", "idMarca")
puis :
CREATE OR REPLACE FUNCTION insert_new_articulo( nombrearticulo text, descripcion text, idtipo int, idfamilia bigint, artstock int, minstock int, maxstock int, idmarca bigint, precio real, marcastock int)
RETURNS boolean
LANGUAGE plpgsql AS
$func$
DECLARE
_id_articulo "Articulo"."idArticulo"%TYPE;
BEGIN
LOOP
SELECT "idArticulo" INTO _id_articulo
FROM "Articulo"
WHERE "Nombre" = $1 AND "idTipo" = $3 AND "idFamilia" = $4;
EXIT WHEN FOUND;
INSERT INTO "Articulo"("Nombre", "Descripcion", "idTipo", "idFamilia", "Stock", "MinStock", "MaxStock")
VALUES ($1, $2, $3, $4, $5, $6, $7)
ON CONFLICT (tag) DO NOTHING
RETURNING "idArticulo" INTO _id_articulo;
EXIT WHEN FOUND;
END LOOP;
LOOP
INSERT INTO "ArticuloMarca"("idArticulo", "idMarca", "PrecioReferencial", "Stock")
VALUES (_id_articulo, $8, $9, $10)
ON CONFLICT ("idArticulo", "idMarca") DO NOTHING;
IF FOUND THEN
RETURN true;
END IF;
IF EXISTS (SELECT FROM "ArticuloMarca"
WHERE "idArticulo" = _id_articulo AND "idMarca" = $8) THEN
RETURN false;
END IF;
END LOOP;
END
$func$;
C'est plus rapide, plus simple et plus fiable. Les boucles ajoutées excluent toute condition de concurrence restante avec des écritures simultanées (tout en n'ajoutant pratiquement aucun coût). Sans écritures simultanées, vous pouvez simplifier. Explication détaillée :
- SELECT ou INSERT est-il dans une fonction sujette à des conditions de concurrence ?
- Comment utiliser RETURNING avec ON CONFLICT dans PostgreSQL ?
À part :utilisez des identificateurs légaux en minuscules pour éviter tous les guillemets bruyants.