PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

A instrução EXECUTE...INTO...USING em PL/pgSQL não pode ser executada em um registro?


Alternativa mais simples para sua resposta postada. Deve ter um desempenho muito melhor.

Esta função recupera uma linha de uma determinada tabela (in_table_name ) e o valor da chave primária (in_row_pk ), e a insere como uma nova linha na mesma tabela, com alguns valores substituídos (in_override_values ). O novo valor da chave primária como padrão é retornado (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;

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

SQL Fiddle.

  • Use regclass como tipo de parâmetro de entrada, portanto, apenas nomes de tabela válidos podem ser usados ​​para começar e a injeção de SQL é descartada. A função também falha mais cedo e com mais facilidade se você fornecer um nome de tabela ilegal.

  • Use um OUT parâmetro (pk_new ) para simplificar a sintaxe.

  • Não há necessidade de descobrir o próximo valor para a chave primária manualmente. Ele é inserido automaticamente e retornado após o fato. Isso não é apenas mais simples e rápido, você também evita números de sequência desperdiçados ou fora de ordem.

  • Use formato() para simplificar a montagem da string de consulta dinâmica e torná-la menos propensa a erros. Observe como eu uso parâmetros posicionais para identificadores e strings, respectivamente.

  • Baseio-me na sua suposição implícita que as tabelas permitidas tenham uma coluna de chave primária única do tipo inteiro com uma coluna padrão . Normalmente serial colunas.

  • O elemento chave da função é o INSERT final :
    • Mesclar valores de substituição com a linha existente usando o #= operador em uma subseleção e decomponha a linha resultante imediatamente.
    • Então você pode selecionar apenas colunas relevantes no SELECT principal .
    • Deixe o Postgres atribuir o valor padrão para o PK e recuperá-lo com o RETURNING cláusula.
    • Escreva o valor retornado no OUT parâmetro diretamente.
    • Tudo feito em um único comando SQL, que geralmente é mais rápido.