Vermeiden Sie Duplikate in der Abfrage INSERT INTO SELECT in SQL Server

109

Ich habe die folgenden zwei Tabellen:

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

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

Ich muss Daten von Table1bis einfügen Table2. Ich kann die folgende Syntax verwenden:

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

In meinem Fall sind jedoch möglicherweise doppelte IDs vorhanden Table2(in meinem Fall ist es nur " 1"), und ich möchte diese nicht erneut kopieren, da dies einen Fehler auslösen würde.

Ich kann so etwas schreiben:

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

Gibt es einen besseren Weg, dies ohne Verwendung zu tun IF - ELSE? Ich möchte zwei INSERT INTO-SELECTAussagen vermeiden , die auf einer bestimmten Bedingung beruhen.

Ashish Gupta
quelle

Antworten:

201

Verwenden von 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)

Verwenden von 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)

Verwenden von 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

Von den drei Optionen ist die LEFT JOIN/IS NULLweniger effizient. Siehe diesen Link für weitere Details .

OMG Ponys
quelle
9
Nur zur Verdeutlichung der NOT EXISTS-Version benötigen Sie einen WITH (HOLDLOCK) -Hinweis, da sonst keine Sperren vorgenommen werden (da keine zu sperrenden Zeilen vorhanden sind!), Damit ein anderer Thread die Zeile unter Ihnen einfügen kann.
IDisposable
3
Interessant, weil ich immer geglaubt habe, dass das Beitreten schneller ist als das Unterwählen. Möglicherweise gilt dies nur für gerade Verknüpfungen und nicht für linke Verknüpfungen.
Duncan
1
Duncan, das Beitreten ist oft schneller als die Unterauswahl, wenn es sich um korrelierte Unterabfragen handelt. Wenn Sie die Unterabfrage in der Auswahlliste haben, ist ein Join oft schneller.
HLGEM
9
NOT EXISTSist besonders nützlich mit zusammengesetzten Primärschlüssel, NOT INfunktioniert dann nicht
Tomash
1
@OMGPonies - Ihr Link für weitere Details scheint tot zu sein. Haben Sie eine andere, die nützlich sein könnte?
FreeMan
36

In MySQL können Sie dies tun:

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

Hat SQL Server etwas Ähnliches?

Duncan
quelle
5
+1 für die Aufklärung. Sehr schöne Syntax. Auf jeden Fall kürzer und besser als der, den ich benutzt habe. Leider hat SQL Server dies nicht.
Ashish Gupta
13
Nicht ganz richtig. Wenn Sie einen eindeutigen Index erstellen, können Sie festlegen, dass "Duplikate ignorieren". In diesem Fall ignoriert SQL Server alle Versuche, ein Duplikat hinzuzufügen.
IamIC
2
Und SQL Server kann immer noch nicht ... erbärmlich.
Smack Jack
1
Also kann SQL Server immer noch nicht?
Ingus
7

Ich hatte gerade ein ähnliches Problem, das Schlüsselwort DISTINCT wirkt magisch:

INSERT INTO Table2(Id, Name) SELECT DISTINCT Id, Name FROM Table1
Hunter Bingham
quelle
20
Es sei denn , ich völlig falsch verstehen Sie, das wird funktionieren , wenn Sie Duplikate in dem Satz haben Sie das Einfügen von . Es hilft jedoch nicht, wenn der Satz, aus dem Sie einfügen, möglicherweise Duplikate von Daten enthält, die bereits in der insert intoTabelle enthalten sind.
FreeMan
5

Ich hatte kürzlich das gleiche Problem ... Hier ist,
was in MS SQL Server 2017 für mich funktioniert hat ...
Der Primärschlüssel sollte für die ID in Tabelle 2 festgelegt werden ...
Die Spalten und Spalteneigenschaften sollten natürlich zwischen beiden gleich sein Tabellen. Dies funktioniert, wenn Sie das folgende Skript zum ersten Mal ausführen. Die doppelte ID in Tabelle 1 wird nicht eingefügt ...

Wenn Sie es das zweite Mal ausführen, erhalten Sie eine

Verletzung des PRIMARY KEY-Einschränkungsfehlers

Dies ist der Code:

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

Die Verwendung ignore Duplicatesdes von IanC vorgeschlagenen eindeutigen Index war meine Lösung für ein ähnliches Problem: Erstellen des Index mit der OptionWITH IGNORE_DUP_KEY

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

Ref.: Index_option

Tazz602
quelle
4

In SQL Server können Sie einen eindeutigen Schlüsselindex für die Tabelle festlegen für (Spalten, die eindeutig sein müssen).

Klicken Sie auf dem SQL Server mit der rechten Maustaste auf das Tabellendesign und wählen Sie Indizes / Schlüssel

Wählen Sie Spalten aus, die nicht dupliziert werden sollen, und geben Sie dann Unique Key ein

M. Salah
quelle
1

Ein wenig abseits des Themas, aber wenn Sie die Daten in eine neue Tabelle migrieren möchten und die möglichen Duplikate in der Originaltabelle enthalten sind und die möglicherweise duplizierte Spalte keine ID ist, reicht GROUP BYFolgendes aus:

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

Ein einfaches DELETEvor dem INSERTwürde genügen:

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

Wechsel Table1für Table2je nachdem , welchen Tisch Idund namePaarung Sie erhalten wollen.

Sacro
quelle
3
Bitte tu das nicht. Sie sagen im Grunde: "Was auch immer meine Daten wertlos sind, fügen Sie einfach diese neuen Daten ein!"
Andir
@Andir Wenn "Table2" aus irgendeinem Grund nach dem "INSERT" nicht gelöscht werden sollte, verwenden Sie die anderen Methoden. Dies ist jedoch ein absolut gültiger Weg, um das zu erreichen, was das OP verlangt.
Sacro
1
Gültig, aber sicherlich langsamer und möglicherweise fehlerhaft ohne Transaktion. Wenn Sie diesen Weg gehen, schließen Sie eine TRANSaction ein.
MC9000