PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

SELECT ou PERFORM dans une fonction PL/pgSQL

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 du INSERT au lieu d'un SELECT 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.