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

L'instruction EXECUTE...INTO...USING en PL/pgSQL ne peut pas s'exécuter dans un enregistrement ?

Alternative plus simple à votre réponse publiée. Devrait être beaucoup plus performant.

Cette fonction récupère une ligne d'une table donnée (in_table_name ) et la valeur de la clé primaire (in_row_pk ), et l'insère en tant que nouvelle ligne dans la même table, avec certaines valeurs remplacées (in_override_values ). La nouvelle valeur de clé primaire par défaut est renvoyée (pk_new ).

CREATE OR REPLACE FUNCTION f_clone_row(in_table_name regclass
                                     , in_row_pk int
                                     , in_override_values hstore
                                     , OUT pk_new int) AS
$func$
DECLARE
   _pk   text;  -- name of PK column
   _cols text;  -- list of names of other columns
BEGIN

-- Get name of PK column
SELECT INTO _pk  a.attname
FROM   pg_catalog.pg_index     i
JOIN   pg_catalog.pg_attribute a ON a.attrelid = i.indrelid
                                AND a.attnum   = i.indkey[0]  -- 1 PK col!
WHERE  i.indrelid = 't'::regclass
AND    i.indisprimary;

-- Get list of columns excluding PK column
_cols := array_to_string(ARRAY(
      SELECT quote_ident(attname)
      FROM   pg_catalog.pg_attribute
      WHERE  attrelid = in_table_name -- regclass used as OID
      AND    attnum > 0               -- exclude system columns
      AND    attisdropped = FALSE     -- exclude dropped columns
      AND    attname <> _pk           -- exclude PK column
      ), ',');

-- INSERT cloned row with override values, returning new PK
EXECUTE format('
   INSERT INTO %1$I (%2$s)
   SELECT %2$s
   FROM  (SELECT (t #= $1).* FROM %1$I t WHERE %3$I = $2) x
   RETURNING %3$I'
 , in_table_name, _cols, _pk)
USING   in_override_values, in_row_pk -- use override values directly
INTO    pk_new;                       -- return new pk directly

END
$func$ LANGUAGE plpgsql;

Appel :

SELECT f_clone_row('t', 1, '"col1"=>"foo_new","col2"=>"bar_new"'::hstore);

SQL Fiddle.

  • Utilisez regclass comme type de paramètre d'entrée, seuls les noms de table valides peuvent donc être utilisés pour commencer et l'injection SQL est exclue. La fonction échoue également plus tôt et plus gracieusement si vous devez fournir un nom de table illégal.

  • Utiliser un OUT paramètre (pk_new ) pour simplifier la syntaxe.

  • Pas besoin de déterminer manuellement la valeur suivante pour la clé primaire. Il est inséré automatiquement et renvoyé après coup. C'est non seulement plus simple et plus rapide, mais vous évitez également les numéros de séquence inutiles ou dans le désordre.

  • Utilisez format() pour simplifier l'assemblage de la chaîne de requête dynamique et la rendre moins sujette aux erreurs. Notez comment j'utilise les paramètres positionnels pour les identificateurs et les chaînes respectivement.

  • Je m'appuie sur votre hypothèse implicite que les tables autorisées ont une colonne de clé primaire unique de type entier avec une colonne par défaut . Typiquement serial colonnes.

  • L'élément clé de la fonction est le dernier INSERT :

    • Fusionnez les valeurs de remplacement avec la ligne existante à l'aide de #= opérateur dans une sous-sélection et décompose immédiatement la ligne résultante.
    • Ensuite, vous pouvez sélectionner uniquement les colonnes pertinentes dans le SELECT principal .
    • Laissez Postgres attribuer la valeur par défaut pour le PK et récupérez-la avec le RETURNING clause.
    • Écrivez la valeur renvoyée dans le OUT paramètre directement.
    • Tout est fait en une seule commande SQL, ce qui est généralement le plus rapide.