Dalam kode plpgsql, SELECT
tanpa target memicu kesalahan. Tapi Anda jelas tidak ingin SELECT INTO
, Anda hanya ingin mengatur status FOUND
. Anda akan menggunakan PERFORM
untuk itu.
- SELECT memunculkan pengecualian dalam fungsi PL/pgSQL
Lebih baik, namun , gunakan IF EXISTS ...
. Pertimbangkan penulisan ulang fungsi Anda ini:
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$;
Tentang EXISTS
:
- PL/pgSQL memeriksa apakah ada baris
Poin utama lainnya :
- Gunakan
RETURNING
klausa dariINSERT
pernyataan alih-alihSELECT
. tambahan .
Postgres 9.5+
Di Postgres 9.5 atau yang lebih baru gunakan INSERT ... ON CONFLICT DO NOTHING
(alias "UPSERT") sebagai gantinya.
Anda akan memiliki UNIQUE
batasan pada "Articulo"("Nombre", "idTipo", "idFamilia")
dan "ArticuloMarca"("idArticulo", "idMarca")
lalu:
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$;
Ini lebih cepat, lebih sederhana, dan lebih dapat diandalkan. Loop yang ditambahkan mengesampingkan kondisi balapan yang tersisa dengan penulisan bersamaan (sambil menambahkan hampir tidak ada biaya). Tanpa penulisan bersamaan, Anda dapat menyederhanakan. Penjelasan detail:
- Apakah SELECT atau INSERT dalam fungsi rentan terhadap kondisi balapan?
- Bagaimana cara menggunakan RETURNING dengan ON CONFLICT di PostgreSQL?
Selain:gunakan pengidentifikasi huruf kecil yang legal untuk menghindari semua tanda kutip ganda yang berisik.