Suponha que eu tenha uma tabela com o seguinte nome people
, onde id
é uma chave primária:
+-----------+---------+---------+
| id | fname | lname |
| (integer) | (text) | (text) |
+===========+=========+=========+
| 1 | Daniel | Edwards |
| 2 | Fred | Holt |
| 3 | Henry | Smith |
+-----------+---------+---------+
Estou tentando escrever uma consulta de duplicação de linha que seja robusta o suficiente para levar em consideração as alterações de esquema na tabela. Sempre que adiciono uma coluna à tabela, não quero ter que voltar e modificar a consulta de duplicação.
Eu sei que posso fazer isso, o que irá duplicar o id do registro 2 e dar ao registro duplicado um novo id:
INSERT INTO people (fname, lname) SELECT fname, lname FROM people WHERE id = 2;
No entanto, se eu adicionar uma age
coluna, precisarei modificar a consulta para também contabilizar a coluna de idade.
Obviamente, não posso fazer o seguinte, porque também duplicará a chave primária, resultando em um duplicate key value violates unique constraint
-- E não quero que eles compartilhem o mesmo id de qualquer maneira:
INSERT INTO people SELECT * FROM people WHERE id = 2
Com isso dito, qual seria uma abordagem razoável para resolver esse desafio? Eu preferiria ficar longe das stored procedures, mas não sou 100% contra elas, suponho...
Simples com
hstore
Se você tiver o módulo adicional
hstore
instalado ( instruções no link abaixo ), existe uma maneira surpreendentemente simples de substituir o(s) valor(es) do(s) campo(s) individual(is) sem saber nada sobre outras colunas:Exemplo básico: duplique a linha com,
id = 2
mas substitua2
por3
:Detalhes:
Supondo (já que não está definido na pergunta) que
people.id
é umaserial
coluna com uma sequência anexada, você desejará o próximo valor da sequência. Podemos determinar o nome da sequência compg_get_serial_sequence()
. Detalhes:Ou você pode apenas codificar o nome da sequência se ele não for alterado.
Teríamos esta consulta:
O que funciona , mas sofre de uma fraqueza no planejador de consulta do Postgres: a expressão é avaliada separadamente para cada coluna na linha, desperdiçando números de sequência e desempenho. Para evitar isso, mova a expressão para uma subqery e decomponha a linha apenas uma vez :
Provavelmente o mais rápido para uma única (ou poucas) linha(s) de uma só vez.
json/jsonb
Se você não
hstore
instalou e não pode instalar módulos adicionais, pode fazer um truque semelhante comjson_populate_record()
oujsonb_populate_record()
, mas esse recurso não está documentado e pode não ser confiável. Atualização: O recurso também está documentado desde o Postgres 13. Veja:Tabela temporária transitória
Outra solução simples seria usar um transiente temporário como este:
Adicionei
ON COMMIT DROP
para dropar a tabela automaticamente ao final da transação. Consequentemente, também envolvi a operação em uma transação própria. Nenhum dos dois é estritamente necessário.Isso oferece uma ampla variedade de opções adicionais - você pode fazer qualquer coisa com a linha antes de inserir, mas será um pouco mais lento devido à sobrecarga de criar e descartar uma tabela temporária.
Esta solução funciona para uma única linha ou para qualquer número de linhas de uma só vez . Cada linha obtém um novo valor padrão da sequência automaticamente.
Usando a notação curta (padrão SQL)
TABLE people
.SQL dinâmico
Para muitas linhas de uma só vez, o SQL dinâmico será mais rápido. Concatene as colunas da tabela do sistema
pg_attribute
ou do esquema de informações e execute-as dinamicamente em umaDO
instrução ou escreva uma função para uso repetido:Ligar:
Funciona para qualquer tabela com uma coluna inteira chamada
id
. Você também pode facilmente tornar o nome da coluna dinâmico ...Talvez não seja sua primeira escolha desde que você queria
stay away from stored procedures
, mas, novamente, não é um "procedimento armazenado" de qualquer maneira ...Relacionado:
Solução avançada
Uma
serial
coluna é um caso especial. Se você quiser preencher mais ou todas as colunas com seus respectivos valores padrão, fica mais sofisticado. Considere esta resposta relacionada:Tente criar um
trigger
on insert:Nesta trigger você torna o ID NULL. Quando o gatilho terminar, a inserção é feita e o Postgres fornecerá um ID. Presumo que você tenha definido o ID como
DEFAULT NEXTVAL('A_SEQUENCE'::REGCLASS)
.SQL dinâmico Funciona muito bem, estou procurando por isso há alguns anos,
se você tiver mais de uma coluna excluída, tente simplesmente,