PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

L'istruzione EXECUTE...INTO...USING in PL/pgSQL non può essere eseguita in un record?

Alternativa più semplice alla tua risposta pubblicata. Dovrebbe funzionare molto meglio.

Questa funzione recupera una riga da una determinata tabella (in_table_name ) e il valore della chiave primaria (in_row_pk ), e la inserisce come nuova riga nella stessa tabella, con alcuni valori sostituiti (in_override_values ). Viene restituito il nuovo valore della chiave primaria per impostazione predefinita (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;

Chiama:

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

SQL Fiddle.

  • Usa regclass come tipo di parametro di input, quindi è possibile utilizzare solo nomi di tabella validi all'inizio e l'iniezione SQL è esclusa. La funzione fallisce anche prima e con maggiore grazia se dovessi fornire un nome di tabella illegale.

  • Usa un OUT parametro (pk_new ) per semplificare la sintassi.

  • Non è necessario calcolare manualmente il valore successivo per la chiave primaria. Viene inserito automaticamente e restituito a posteriori. Questo non è solo più semplice e veloce, ma eviti anche numeri di sequenza sprecati o fuori ordine.

  • Usa format() per semplificare l'assemblaggio della stringa di query dinamica e renderla meno soggetta a errori. Nota come utilizzo i parametri posizionali rispettivamente per identificatori e stringhe.

  • Mi baso sulla tua ipotesi implicita che le tabelle consentite hanno una colonna di chiave primaria singola di tipo intero con una colonna predefinita . Tipicamente serial colonne.

  • L'elemento chiave della funzione è il INSERT finale :

    • Unisci i valori di sostituzione con la riga esistente utilizzando #= operatore in una sottoselezione e scomponi immediatamente la riga risultante.
    • Quindi puoi selezionare solo le colonne pertinenti nel SELECT principale .
    • Lascia che Postgres assegni il valore predefinito per il PK e lo restituisca con il RETURNING clausola.
    • Scrivi il valore restituito in OUT parametro direttamente.
    • Tutto fatto con un unico comando SQL, generalmente più veloce.