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

O que acontece com duplicatas ao inserir várias linhas?


O INSERT apenas inserirá todas as linhas e nada especial acontecerá, a menos que você tem algum tipo de restrição não permitindo valores duplicados/sobrepostos (PRIMARY KEY , UNIQUE , CHECK ou EXCLUDE restrição) - que você não mencionou em sua pergunta. Mas é com isso que você provavelmente está preocupado.

Assumindo um UNIQUE ou restrição PK em (col1,col2) , você está lidando com um livro UPSERT situação. Muitas perguntas e respostas relacionadas para encontrar aqui.

Geralmente, se qualquer restrição é violada, uma exceção é gerada que (a menos que seja presa em subtransação como é possível em uma linguagem procedural do lado do servidor como plpgsql) reverterá não apenas a instrução, mas a transação inteira .

Sem gravações simultâneas


Ou seja:Nenhuma outra transação tentará gravar na mesma tabela ao mesmo tempo.

  • Excluir linhas que já estão na tabela com WHERE NOT EXISTS ... ou qualquer outra técnica aplicável:

  • Selecione linhas que não estão presentes em outra tabela

  • E não se esqueça de remover duplicatas dentro o conjunto inserido também, o que não ser excluído pelo semi-anti-join WHERE NOT EXISTS ...

Uma técnica para lidar com ambos ao mesmo tempo seria EXCEPT :
INSERT INTO tbl (col1, col2)
VALUES
  (text 'v1', text 'v2')  -- explicit type cast may be needed in 1st row
, ('v3', 'v4')
, ('v3', 'v4')  -- beware of dupes in source
EXCEPT SELECT col1, col2 FROM tbl;

EXCEPT sem a palavra chave ALL dobra linhas duplicadas na fonte. Se você sabe que não há dupes ou não deseja dobrar duplicatas silenciosamente, use EXCEPT ALL (ou uma das outras técnicas). Ver:
  • Usando a cláusula EXCEPT no PostgreSQL

Geralmente, se a tabela de destino for grande , WHERE NOT EXISTS em combinação com DISTINCT na fonte provavelmente será mais rápido:
INSERT INTO tbl (col1, col2)
SELECT *
FROM  (
   SELECT DISTINCT *
   FROM  (
       VALUES
         (text 'v1', text'v2')
       , ('v3', 'v4')
       , ('v3', 'v4')  -- dupes in source
      ) t(c1, c2)
   ) t
WHERE NOT EXISTS (
   SELECT FROM tbl
   WHERE  col1 = t.c1 AND col2 = t.c2
   );

Se pode haver muitos trapaceiros, vale a pena dobrá-los na fonte primeiro. Caso contrário, use uma subconsulta a menos.

Relacionado:
  • Selecione as linhas que não estão presentes em outra tabela

Com gravações simultâneas


Use o UPSERT do Postgres implementação INSERT ... ON CONFLICT ... no Postgres 9.5 ou mais tarde:
INSERT INTO tbl (col1,col2)
SELECT DISTINCT *  -- still can't insert the same row more than once
FROM  (
   VALUES
     (text 'v1', text 'v2')
   , ('v3','v4')
   , ('v3','v4')  -- you still need to fold dupes in source!
  ) t(c1, c2)
ON CONFLICT DO NOTHING;  -- ignores rows with *any* conflict!

Leitura adicional:
  • Como usar RETURNING com ON CONFLICT no PostgreSQL?
  • Como insiro uma linha que contém uma chave estrangeira?

Documentação:
  • Manual
  • A página de confirmação
  • A página do Postgres Wiki

Resposta de referência de Craig para UPSERT problemas:
  • Como UPSERT (MERGE, INSERT ... ON DUPLICATE UPDATE) no PostgreSQL?