13

我的数据库中有这个功能:

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 来创建它会怎样?关于这个问题的任何想法?

4

2 回答 2

17

在 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

另一个重点

Postgres 9.5+

在 Postgres 9.5 或更高版本中使用INSERT ... ON CONFLICT DO NOTHING(又名“UPSERT”)代替。
你会对和然后有UNIQUE限制:"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$;

这更快、更简单、更可靠。添加的循环排除了并发写入的任何剩余竞争条件(同时几乎不增加任何成本)。如果没有并发写入,您可以简化。详细解释:

另外:使用合法的小写标识符来避免所有嘈杂的双引号

于 2012-04-12T03:31:42.607 回答
4

这条线在我看来很可疑,可能是导致你悲伤的原因:

SELECT * FROM "ArticuloMarca" AS am WHERE am."idArticulo" = articulo."idArticulo" and am."idMarca" = $8;

您正在函数中执行 SELECT,但没有对结果做任何事情。您需要像之前在函数中所做的那样执行 SELECT INTO。

于 2012-04-12T02:46:26.203 回答