AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 122120
Accepted
Joshua Burns
Joshua Burns
Asked: 2015-11-26 10:03:26 +0800 CST2015-11-26 10:03:26 +0800 CST 2015-11-26 10:03:26 +0800 CST

Linha duplicada com chave primária no PostgreSQL

  • 772

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 agecoluna, 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...

postgresql postgresql-9.4
  • 3 3 respostas
  • 21764 Views

3 respostas

  • Voted
  1. Best Answer
    Erwin Brandstetter
    2015-11-26T19:58:26+08:002015-11-26T19:58:26+08:00

    Simples comhstore

    Se você tiver o módulo adicional hstoreinstalado ( 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 = 2mas substitua 2por 3:

    INSERT INTO people
    SELECT (p #= hstore('id', '3')).* FROM people p WHERE id = 2;
    

    Detalhes:

    • Como definir o valor do campo variável composto usando SQL dinâmico
    • Atribuir a NEW por chave em um gatilho Postgres

    Supondo (já que não está definido na pergunta) quepeople.idé umaserialcoluna 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:

    • PostgreSQL SELECT chave primária como "serial" ou "bigserial"

    Ou você pode apenas codificar o nome da sequência se ele não for alterado.
    Teríamos esta consulta:

    INSERT INTO people
    SELECT (p #= hstore('id', nextval(pg_get_serial_sequence('people', 'id'))::text)).*
    FROM people p WHERE id = 2;
    

    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 :

    INSERT INTO people
    SELECT (p1).*
    FROM  (
       SELECT p #= hstore('id', nextval(pg_get_serial_sequence('people', 'id'))::text) AS p1
       FROM   people p WHERE id = 2
       ) sub;
    

    Provavelmente o mais rápido para uma única (ou poucas) linha(s) de uma só vez.

    json/jsonb

    Se você não hstoreinstalou e não pode instalar módulos adicionais, pode fazer um truque semelhante com json_populate_record()ou jsonb_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:

    • Como definir o valor do campo variável composto usando SQL dinâmico

    Tabela temporária transitória

    Outra solução simples seria usar um transiente temporário como este:

    BEGIN;
    CREATE TEMP TABLE people_tmp ON COMMIT DROP AS
    SELECT * FROM people WHERE id = 2;
    UPDATE people_tmp SET id = nextval(pg_get_serial_sequence('people', 'id'));
    INSERT INTO people TABLE people_tmp;
    COMMIT;
    

    Adicionei ON COMMIT DROPpara 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_attributeou do esquema de informações e execute-as dinamicamente em uma DOinstrução ou escreva uma função para uso repetido:

    CREATE OR REPLACE FUNCTION f_row_copy(_tbl regclass, _id int, OUT row_ct int)
      LANGUAGE plpgsql AS
    $func$
    BEGIN
       EXECUTE (
          SELECT format('INSERT INTO %1$s(%2$s) SELECT %2$s FROM %1$s WHERE id = $1',
                        _tbl, string_agg(quote_ident(attname), ', '))
          FROM   pg_attribute
          WHERE  attrelid = _tbl
          AND    NOT attisdropped  -- no dropped (dead) columns
          AND    attnum > 0        -- no system columns
          AND    attname <> 'id'   -- exclude id column
          )
       USING _id;
    
       GET DIAGNOSTICS row_ct = ROW_COUNT;  -- directly assign OUT parameter
    END
    $func$;
    

    Ligar:

    SELECT f_row_copy('people', 9);
    

    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:

    • psql: SELECT * ... exceto uma coluna
    • Listar todas as colunas de uma tabela especificada

    Solução avançada

    Uma serialcoluna é 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:

    • Gere valores DEFAULT em um CTE UPSERT usando o PostgreSQL 9.3
    • 20
  2. Marco
    2015-11-26T10:25:52+08:002015-11-26T10:25:52+08:00

    Tente criar um triggeron insert:

    CREATE TRIGGER name BEFORE 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).

    • 0
  3. gouly
    2017-11-16T22:58:22+08:002017-11-16T22:58:22+08:00

    SQL dinâmico Funciona muito bem, estou procurando por isso há alguns anos,

    se você tiver mais de uma coluna excluída, tente simplesmente,

    AND    attname <> 'id'   -- exclude id column
    AND    attname <> 'second_col_name'   -- exclude second_col_name 
    
    • -2

relate perguntas

  • Posso ativar o PITR depois que o banco de dados foi usado

  • Práticas recomendadas para executar a replicação atrasada do deslocamento de tempo

  • Os procedimentos armazenados impedem a injeção de SQL?

  • Sequências Biológicas do UniProt no PostgreSQL

  • Qual é a diferença entre a replicação do PostgreSQL 9.0 e o Slony-I?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve