我的数据库中有这个函数:
CREATE OR REPLACE FUNCTION "insertarNuevoArticulo"(nombrearticulo character varying, descripcion text, idtipo integer, idfamilia bigint, artstock integer, minstock integer, maxstock integer, idmarca bigint, precio real, marcastock integer)
RETURNS boolean AS
$BODY$
DECLARE
articulo "Articulo"%ROWTYPE;
BEGIN
SELECT * INTO articulo FROM "Articulo" WHERE "Nombre" = $1 AND "idTipo"=$3 AND "idFamilia"=$4;
IF NOT FOUND THEN
INSERT INTO "Articulo" ("Nombre", "Descripcion", "idTipo", "idFamilia", "Stock", "MinStock", "MaxStock") Values ($1, $2, $3, $4, $5, $6, $7);
SELECT last_value
INTO articulo."idArticulo"
FROM "public"."Articulo_idArticulo_seq";
END IF;
SELECT * FROM "ArticuloMarca" AS am WHERE am."idArticulo" = articulo."idArticulo" and am."idMarca" = $8;
IF NOT FOUND THEN
Insert into "ArticuloMarca"("idArticulo", "idMarca", "PrecioReferencial", "Stock") Values (articulo."idArticulo", $8, $9, $10);
RETURN TRUE;
END IF;
RETURN FALSE;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION "insertarNuevoArticulo"(character varying, text, integer, bigint, integer, integer, integer, bigint, real, integer)
OWNER TO postgres;
但是当我尝试使用它时,它说如果我想丢弃结果,我需要使用 PERFORM
!这里的问题是我不想!我希望它们位于我声明的 articulo
行中!
我正在使用这个语句:
SELECT "insertarNuevoArticulo"('Acetaminofen', 'caro', '1' , '1', '8', '1', '10', '1', '150.7', '10');
我得到的错误是 42601,一个语法错误!如果我用IDE创建它会怎样?对这个问题有什么想法吗?
最佳答案
在 plpgsql 代码中,没有目标的 SELECT
会触发错误。但是您显然不想要SELECT INTO
,您只想设置FOUND
的状态。你会使用 PERFORM
为此。
更好的是,使用IF EXISTS ...
。考虑重写您的函数:
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$;
关于 EXISTS
:
其他要点:
- 使用
RETURNING
clauseINSERT
语句,而不是额外的SELECT
。
Postgres 9.5+
在 Postgres 9.5 或更高版本中,使用 INSERT ... ON CONFLICT DO NOTHING
(也称为“UPSERT”)。
您将对 "Articulo"("Nombre", "idTipo", "idFamilia")
和 "ArticuloMarca"("idArticulo", "idMarca"")
然后:
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$;
这样更快、更简单、更可靠。添加的循环排除了并发写入的任何剩余竞争条件(同时几乎不增加任何成本)。没有并发写入,你可以简化。详细解释:
- Is SELECT or INSERT in a function prone to race conditions?
- How to use RETURNING with ON CONFLICT in PostgreSQL?
另外:使用合法的小写标识符来避免所有嘈杂的double-quotes .
关于postgresql - PL/pgSQL 函数中的 SELECT 或 PERFORM,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10115806/