Unikaj duplikatów w kwerendzie INSERT INTO SELECT w programie SQL Server

109

Mam następujące dwie tabele:

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

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

Muszę wstawić dane od Table1do Table2. Mogę użyć następującej składni:

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

Jednak w moim przypadku zduplikowane identyfikatory mogą istnieć w Table2(w moim przypadku jest to po prostu „ 1”) i nie chcę ich ponownie kopiować, ponieważ spowodowałoby to błąd.

Mogę napisać coś takiego:

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

Czy jest lepszy sposób na zrobienie tego bez używania IF - ELSE? Chcę uniknąć dwóch INSERT INTO-SELECTstwierdzeń opartych na jakimś warunku.

Ashish Gupta
źródło

Odpowiedzi:

201

Używając 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)

Używając 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)

Używając 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

Z trzech opcji LEFT JOIN/IS NULLjest mniej wydajna. Zobacz ten link, aby uzyskać więcej informacji .

Kucyki OMG
źródło
9
Tylko wyjaśnienie dotyczące wersji NIE ISTNIEJE, będziesz potrzebować podpowiedzi Z (HOLDLOCK) lub nie zostaną podjęte żadne blokady (ponieważ nie ma żadnych wierszy do zablokowania!), Aby inny wątek mógł wstawić wiersz pod tobą.
IDisposable
3
Interesujące, ponieważ zawsze uważałem, że dołączanie jest szybsze niż sub-selekcje. Być może dotyczy to tylko złączeń prostych i nie dotyczy złączeń lewostronnych.
Duncan,
1
Duncan, łączenie jest często szybsze niż podwybór, gdy są skorelowanymi podzapytaniami. Jeśli masz podzapytanie na liście wyboru, łączenie często będzie szybsze.
HLGEM,
9
NOT EXISTSjest szczególnie przydatny ze złożonym kluczem podstawowym, NOT INnie zadziała wtedy
tomash
1
@OMGPonies - Twój link, aby uzyskać więcej informacji, wydaje się być martwy. Czy masz inny, który może być przydatny?
FreeMan
36

W MySQL możesz to zrobić:

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

Czy SQL Server ma coś podobnego?

Duncan
źródło
5
+1 za poinformowanie mnie o tym. Bardzo ładna składnia. Zdecydowanie krótszy i lepszy niż ten, którego używałem. Niestety serwer Sql nie ma tego.
Ashish Gupta,
13
Nie do końca prawda. Kiedy tworzysz unikalny indeks, możesz ustawić go na „ignoruj ​​duplikaty”, w którym to przypadku SQL Server będzie ignorował wszelkie próby dodania duplikatu.
IamIC
2
A SQL Server nadal nie może ... żałosne.
Smack Jacka
1
Więc SQL Server nadal nie może?
Ingus
8

Właśnie miałem podobny problem, słowo kluczowe DISTINCT działa magicznie:

INSERT INTO Table2(Id, Name) SELECT DISTINCT Id, Name FROM Table1
Hunter Bingham
źródło
21
Chyba, że zupełnie nie rozumieją ty, to będzie działać, jeśli masz duplikatów w zestawie masz wkładając od . Nie pomoże to jednak, jeśli zestaw, z którego wstawiasz, może być duplikatem danych znajdujących się już w insert intotabeli.
FreeMan
5

Ostatnio miałem ten sam problem ...
Oto co działało u mnie w MS SQL serwerze 2017 ...
Klucz podstawowy powinien być ustawiony na ID w tabeli 2 ...
Kolumny i właściwości kolumn powinny być oczywiście takie same w obu stoły. To zadziała przy pierwszym uruchomieniu poniższego skryptu. Powielony identyfikator w tabeli 1 nie wstawi ...

Jeśli uruchomisz go po raz drugi, otrzymasz plik

Naruszenie błędu ograniczenia PRIMARY KEY

To jest kod:

Insert into Table_2
Select distinct *
from Table_1
where table_1.ID >1
Vishane Naicker
źródło
4

Korzystanie ignore Duplicatesz unikalnego indeksu, zgodnie z sugestią IanC, było moim rozwiązaniem podobnego problemu, tworząc indeks z opcjąWITH IGNORE_DUP_KEY

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

Ref .: opcja_indeksu

Tazz602
źródło
4

W SQL Server możesz ustawić unikalny indeks klucza w tabeli dla (Kolumny, które muszą być unikalne)

Na serwerze sql kliknij prawym przyciskiem myszy projekt tabeli i wybierz Indeksy / klucze

Wybierz kolumny, które nie będą zduplikowane, a następnie wpisz unikalny klucz

M. Salah
źródło
1

Trochę nie na temat, ale jeśli chcesz przenieść dane do nowej tabeli, a możliwe duplikaty znajdują się w oryginalnej tabeli , a kolumna prawdopodobnie zduplikowana nie jest identyfikatorem, GROUP BYzrobi to:

INSERT INTO TABLE_2
(name)
  SELECT t1.name
  FROM TABLE_1 t1
  GROUP BY t1.name
FullStackFool
źródło
-1

Wystarczyłoby proste, DELETEzanim INSERT:

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

Przełączanie Table1w Table2zależności od tego, który stół Idi nameparowanie chcesz zachować.

Sacro
źródło
3
Proszę, nie rób tego. Mówisz po prostu: „jakiekolwiek dane, które miałem, są bezwartościowe, po prostu wstawmy te nowe!”
Andir
@Andir Jeśli z jakiegoś powodu „Table2” nie powinno zostać porzucone po „INSERT”, użyj innych metod, ale jest to całkowicie poprawny sposób osiągnięcia tego, o co prosił OP.
Sacro
1
Prawidłowe, ale z pewnością wolniejsze i potencjalnie szkodliwe bez transakcji. Jeśli wybierzesz tę trasę, zawiń TRANSAKCJĘ.
MC9000