Evita i duplicati nella query INSERT INTO SELECT in SQL Server


109

Ho le seguenti due tabelle:

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

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

Devo inserire dati da Table1a Table2. Posso usare la seguente sintassi:

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

Tuttavia, nel mio caso, potrebbero esistere ID duplicati in Table2(nel mio caso, è solo " 1") e non voglio copiarlo di nuovo poiché ciò genererebbe un errore.

Posso scrivere qualcosa del genere:

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

C'è un modo migliore per farlo senza usare IF - ELSE? Voglio evitare due INSERT INTO-SELECTaffermazioni basate su alcune condizioni.

Risposte:


201

Utilizzando 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)

Utilizzando 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)

Utilizzando 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

Delle tre opzioni, LEFT JOIN/IS NULLè meno efficiente. Vedi questo collegamento per maggiori dettagli .


9
Solo un chiarimento sulla versione NOT EXISTS, avrai bisogno di un suggerimento WITH (HOLDLOCK) o non verranno presi blocchi (perché non ci sono righe da bloccare!) In modo che un altro thread possa inserire la riga sotto di te.
IDisponibile il

3
Interessante, perché ho sempre creduto che l'adesione fosse più veloce delle sotto-selezioni. Forse è solo per i join diritti e non è applicabile ai join sinistri.
Duncan

1
Duncan, l'unione è spesso più veloce della sottoselezione quando sono sottoquery correlate. Se hai la sottoquery nell'elenco di selezione, un join sarà spesso più veloce.
HLGEM

9
NOT EXISTSè particolarmente utile con la chiave primaria composita, NOT INnon funzionerà allora
tomash

1
@OMGPonies - il tuo link per maggiori dettagli sembra essere morto. Ne hai un altro che potrebbe esserti utile?
FreeMan

36

In MySQL puoi fare questo:

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

SQL Server ha qualcosa di simile?


5
+1 per avermi istruito su questo. Sintassi molto bella. Decisamente più corto e migliore di quello che ho usato. Purtroppo il server SQL non ha questo.
Ashish Gupta

13
Non del tutto vero. Quando crei un indice univoco, puoi impostarlo su "ignora duplicati", nel qual caso SQL Server ignorerà qualsiasi tentativo di aggiungere un duplicato.
IamIC

2
E SQL Server non può ancora ... patetico.
Smack Jack

1
Quindi SQL Server non può ancora?
Ingus

8

Ho appena avuto un problema simile, la parola chiave DISTINCT funziona magicamente:

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

21
A meno che non totalmente comprendono male di te, questo funzionerà se si dispone di duplicati del set si sta inserendo da . Tuttavia, non sarà di aiuto se il set da cui stai inserendo potrebbe essere duplicato di dati già nella insert intotabella.
FreeMan

5

Di recente ho riscontrato lo stesso problema ...
Ecco cosa ha funzionato per me in MS SQL Server 2017 ...
La chiave primaria dovrebbe essere impostata sull'ID nella tabella 2 ...
Le colonne e le proprietà delle colonne dovrebbero essere le stesse ovviamente tra entrambi tabelle. Funzionerà la prima volta che esegui lo script seguente. L'ID duplicato nella tabella 1, non inserirà ...

Se lo esegui la seconda volta, otterrai un file

Violazione dell'errore di vincolo PRIMARY KEY

Questo è il codice:

Insert into Table_2
Select distinct *
from Table_1
where table_1.ID >1

4

L'utilizzo ignore Duplicatesdell'indice univoco come suggerito da IanC è stata la mia soluzione per un problema simile, creando l'indice con l'opzioneWITH IGNORE_DUP_KEY

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

Rif .: index_option


4

Da SQL Server è possibile impostare un indice di chiave univoco sulla tabella per (Colonne che devono essere univoche)

Dal server sql fare clic con il pulsante destro del mouse sul design della tabella selezionare Indici / Chiavi

Seleziona le colonne che non saranno duplicate, quindi digita Chiave univoca


1

Un po 'fuori tema, ma se vuoi migrare i dati in una nuova tabella, e i possibili duplicati sono nella tabella originale , e la colonna eventualmente duplicata non è un id, un GROUP BYfarà:

INSERT INTO TABLE_2
(name)
  SELECT t1.name
  FROM TABLE_1 t1
  GROUP BY t1.name

-1

Basterebbe un semplice DELETEprima del INSERT:

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

Passaggio Table1a Table2seconda di quale tabella Ide nameassociazione si desidera preservare.


3
Per favore, non farlo. In pratica stai dicendo "qualunque dato che ho avuto è inutile, inseriamo solo questi nuovi dati!"
Andir

@Andir Se per qualche motivo "Table2" non dovesse essere eliminato dopo "INSERT", allora usa gli altri metodi, ma questo è un modo perfettamente valido per ottenere ciò che l'OP ha chiesto.
Sacro

1
Valido, ma sicuramente più lento e potenzialmente dannoso senza una transazione. Se segui questa strada, inserisci una TRANSAZIONE.
MC9000
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.