I plpgsql-kod, SELECT
utan ett mål utlöser ett fel. Men det gör du uppenbarligen inte vill ha SELECT INTO
, du vill bara ställa in statusen för FOUND
. Du skulle använda PERFORM
för det.
- SELECT höjer undantag i PL/pgSQL-funktionen
Ännu bättre , använd IF EXISTS ...
. Överväg denna omskrivning av din funktion:
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$;
Om EXISTS
:
- PL/pgSQL kontrollerar om en rad finns
Den andra viktiga punkten :
- Använd
RETURNING
satsen iINSERT
sats istället för en ytterligareSELECT
.
Postgres 9.5+
I Postgres 9.5 eller senare använder du INSERT ... ON CONFLICT DO NOTHING
(a.k.a. "UPSERT") istället.
Du skulle ha UNIQUE
begränsningar på "Articulo"("Nombre", "idTipo", "idFamilia")
och "ArticuloMarca"("idArticulo", "idMarca")
och sedan:
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$;
Detta är snabbare, enklare och mer pålitligt. De tillagda slingorna utesluter eventuella återstående tävlingsförhållanden med samtidiga skrivningar (samtidigt som knappast tillför någon kostnad). Utan samtidiga skrivningar kan du förenkla. Detaljerad förklaring:
- Är SELECT eller INSERT i en funktion utsatt för tävlingsförhållanden?
- Hur använder man RETURNING med ON CONFLICT i PostgreSQL?
Bortsett från:använd lagliga identifierare med små bokstäver för att undvika alla bullriga citattecken.