Evite duplicatas na consulta INSERT INTO SELECT no SQL Server

109

Eu tenho as duas tabelas a seguir:

Table1
----------
ID   Name
1    A
2    B
3    C

Table2
----------
ID   Name
1    Z

Preciso inserir dados de Table1para Table2. Posso usar a seguinte sintaxe:

INSERT INTO Table2(Id, Name) SELECT Id, Name FROM Table1

No entanto, no meu caso, podem existir IDs duplicados em Table2(no meu caso, é apenas " 1") e não quero copiá- los novamente, pois isso geraria um erro.

Posso escrever algo assim:

IF NOT EXISTS(SELECT 1 FROM Table2 WHERE Id=1)
INSERT INTO Table2 (Id, name) SELECT Id, name FROM Table1 
ELSE
INSERT INTO Table2 (Id, name) SELECT Id, name FROM Table1 WHERE Table1.Id<>1

Existe uma maneira melhor de fazer isso sem usar IF - ELSE? Quero evitar duas INSERT INTO-SELECTdeclarações com base em alguma condição.

Ashish Gupta
fonte

Respostas:

201

Usando NOT EXISTS:

INSERT INTO TABLE_2
  (id, name)
SELECT t1.id,
       t1.name
  FROM TABLE_1 t1
 WHERE NOT EXISTS(SELECT id
                    FROM TABLE_2 t2
                   WHERE t2.id = t1.id)

Usando NOT IN:

INSERT INTO TABLE_2
  (id, name)
SELECT t1.id,
       t1.name
  FROM TABLE_1 t1
 WHERE t1.id NOT IN (SELECT id
                       FROM TABLE_2)

Usando LEFT JOIN/IS NULL:

INSERT INTO TABLE_2
  (id, name)
   SELECT t1.id,
          t1.name
     FROM TABLE_1 t1
LEFT JOIN TABLE_2 t2 ON t2.id = t1.id
    WHERE t2.id IS NULL

Das três opções, a LEFT JOIN/IS NULLé menos eficiente. Veja este link para mais detalhes .

Pôneis OMG
fonte
9
Apenas um esclarecimento sobre a versão NOT EXISTS, você precisará de uma dica WITH (HOLDLOCK) ou nenhum bloqueio será feito (porque não há linhas para bloquear!), Então outro thread poderia inserir a linha abaixo de você.
IDisponíveis em
3
Interessante, porque sempre acreditei que juntar-se é mais rápido do que sub-selecionar. Talvez seja apenas para junções diretas e não se aplique a junções à esquerda.
Duncan,
1
Duncan, a junção é geralmente mais rápida do que a subseleção quando são subconsultas correlacionadas Se você tiver a subconsulta na lista de seleção, a junção geralmente será mais rápida.
HLGEM
9
NOT EXISTSé especialmente útil com chave primária composta, NOT INnão funcionará então
tomash
1
@OMGPonies - seu link para mais detalhes parece estar morto. Você tem outro que possa ser útil?
FreeMan de
36

No MySQL, você pode fazer isso:

INSERT IGNORE INTO Table2(Id, Name) SELECT Id, Name FROM Table1

O SQL Server tem algo semelhante?

Duncan
fonte
5
1 por me educar sobre isso. Sintaxe muito boa. Definitivamente mais curto e melhor do que o que usei. Infelizmente o servidor Sql não tem isso.
Ashish Gupta
13
Não é totalmente verdade. Ao criar um índice exclusivo, você pode defini-lo para "ignorar duplicatas", caso em que o SQL Server ignorará qualquer tentativa de adicionar uma duplicata.
IamIC
2
E o SQL Server ainda não pode ... patético.
Smack Jack em
1
Então o SQL Server ainda não consegue?
Ingus
8

Acabei de ter um problema semelhante, a palavra-chave DISTINCT funciona como mágica:

INSERT INTO Table2(Id, Name) SELECT DISTINCT Id, Name FROM Table1
Hunter Bingham
fonte
21
A menos que eu totalmente entendê-lo mal, isso vai funcionar se você tiver duplicatas no conjunto que você está inserindo a partir . No entanto, não ajudará se o conjunto do qual você está inserindo possa ser duplicatas de dados já na insert intotabela.
FreeMan
5

Eu estava enfrentando o mesmo problema recentemente ...
Aqui está o que funcionou para mim no MS SQL Server 2017 ...
A chave primária deve ser definida no ID na tabela 2 ...
As colunas e propriedades da coluna devem ser as mesmas, é claro, entre os dois tabelas. Isso funcionará na primeira vez que você executar o script abaixo. O ID duplicado na tabela 1, não irá inserir ...

Se você executá-lo pela segunda vez, obterá um

Violação de erro de restrição PRIMARY KEY

Este é o código:

Insert into Table_2
Select distinct *
from Table_1
where table_1.ID >1
Vishane Naicker
fonte
4

Usando ignore Duplicateso índice exclusivo, conforme sugerido pelo IanC, aqui estava minha solução para um problema semelhante, criando o índice com a opçãoWITH IGNORE_DUP_KEY

In backward compatible syntax
, WITH IGNORE_DUP_KEY is equivalent to WITH IGNORE_DUP_KEY = ON.

Ref .: index_option

Tazz602
fonte
4

No SQL Server, você pode definir um índice de chave exclusivo na tabela para (colunas que precisam ser exclusivas)

No servidor sql, clique com o botão direito no design da tabela e selecione Índices / Chaves

Selecione a (s) coluna (s) que não serão duplicadas e digite a chave exclusiva

M. Salah
fonte
1

Um pouco fora do tópico, mas se você quiser migrar os dados para uma nova tabela, e as possíveis duplicatas estiverem na tabela original , e a coluna possivelmente duplicada não for um id, um GROUP BYfará:

INSERT INTO TABLE_2
(name)
  SELECT t1.name
  FROM TABLE_1 t1
  GROUP BY t1.name
FullStackFool
fonte
-1

Um simples DELETEantes do INSERTseria suficiente:

DELETE FROM Table2 WHERE Id = (SELECT Id FROM Table1)
INSERT INTO Table2 (Id, name) SELECT Id, name FROM Table1

Alternar Table1para Table2dependendo de qual mesa Ide nameemparelhamento você deseja preservar.

Sacro
fonte
3
Por favor, não faça isso. Você está basicamente dizendo "quaisquer dados que eu tenha são inúteis, vamos apenas inserir esses novos dados!"
Andir
@Andir Se por alguma razão a "Tabela2" não deve ser descartada após o "INSERT", então use os outros métodos, mas esta é uma maneira perfeitamente válida de alcançar o que o OP pediu.
Sacro
1
Válido, mas certamente mais lento e potencialmente corrompendo sem uma transação. Se você seguir esse caminho, envolva uma TRANSação.
MC9000