Tránh trùng lặp trong truy vấn CHÈN VÀO CHỌN trong SQL Server


109

Tôi có hai bảng sau:

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

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

Tôi cần chèn dữ liệu từ Table1đến Table2. Tôi có thể sử dụng cú pháp sau:

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

Tuy nhiên, trong trường hợp của tôi, các ID trùng lặp có thể tồn tại Table2(trong trường hợp của tôi, đó chỉ là " 1") và tôi không muốn sao chép lại vì điều đó sẽ gây ra lỗi.

Tôi có thể viết một cái gì đó như thế này:

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ó cách nào tốt hơn để làm điều này mà không cần sử dụng IF - ELSEkhông? Tôi muốn tránh hai INSERT INTO-SELECTtuyên bố dựa trên một số điều kiện.

Câu trả lời:


201

Sử dụng 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)

Sử dụng 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)

Sử dụng 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

Trong ba tùy chọn, tùy chọn nào LEFT JOIN/IS NULLkém hiệu quả hơn. Xem liên kết này để biết thêm chi tiết .


9
Chỉ cần giải thích rõ về phiên bản KHÔNG TỒN TẠI, bạn sẽ cần gợi ý CÓ (KHÓA GIỮ) hoặc không có ổ khóa nào được thực hiện (vì không có hàng nào để khóa!) Để một luồng khác có thể chèn hàng bên dưới bạn.
IDisposable

3
Thật thú vị, bởi vì tôi luôn tin rằng tham gia nhanh hơn các lựa chọn phụ. Có lẽ điều đó chỉ dành cho các phép nối thẳng và không áp dụng cho các phép nối bên trái.
Duncan

1
Duncan, việc kết hợp thường nhanh hơn, điều này sẽ tự chọn khi chúng là các truy vấn con tương quan. Nếu bạn có truy vấn con trong danh sách lựa chọn thì một phép nối thường sẽ nhanh hơn.
HLGEM

9
NOT EXISTSđặc biệt hữu ích với khóa chính tổng hợp, NOT INsau đó sẽ không hoạt động
tomash

1
@OMGPonies - liên kết của bạn để biết thêm chi tiết dường như đã chết. Bạn có cái khác có thể hữu ích không?
Miễn phíMan

36

Trong MySQL, bạn có thể làm điều này:

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

SQL Server có gì tương tự không?


5
+1 để hướng dẫn tôi về điều này. Cú pháp rất hay. Chắc chắn là ngắn hơn và tốt hơn cái tôi đã dùng. Rất tiếc máy chủ Sql không có điều này.
Ashish Gupta

13
Không hoàn toàn đúng. Khi bạn tạo một chỉ mục duy nhất, bạn có thể đặt nó thành "bỏ qua các bản sao", trong trường hợp này, SQL Server sẽ bỏ qua bất kỳ nỗ lực nào để thêm một bản sao.
IamIC

2
Và SQL Server vẫn không thể ... thảm hại.
Smack Jack,

1
Vì vậy, SQL Server vẫn không thể?
Ingus

8

Tôi vừa gặp sự cố tương tự, từ khóa DISTINCT hoạt động kỳ diệu:

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

21
Trừ khi tôi hoàn toàn hiểu sai bạn, điều này sẽ làm việc nếu bạn có bản sao trong tập bạn đang chèn từ . Tuy nhiên, nó sẽ không giúp ích gì nếu tập hợp bạn đang chèn có thể là bản sao của dữ liệu đã có trong insert intobảng.
Miễn phíMan

5

Gần đây tôi đang gặp phải vấn đề tương tự ...
Đây là những gì đã làm việc cho tôi trong MS SQL server 2017 ...
Khóa chính phải được đặt trên ID trong bảng 2 ...
Tất nhiên các cột và thuộc tính cột phải giống nhau giữa cả hai những cái bàn. Điều này sẽ hoạt động vào lần đầu tiên bạn chạy tập lệnh bên dưới. ID trùng lặp trong bảng 1, sẽ không chèn ...

Nếu bạn chạy nó lần thứ hai, bạn sẽ nhận được

Vi phạm lỗi ràng buộc KHÓA CHÍNH

Đây là mã:

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

4

Sử dụng ignore Duplicateschỉ mục duy nhất theo đề xuất của IanC đây là giải pháp của tôi cho một vấn đề tương tự, tạo chỉ mục bằng Tùy chọnWITH IGNORE_DUP_KEY

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

Tham chiếu: index_option


4

Từ SQL Server, bạn có thể đặt chỉ mục khóa Duy nhất trên bảng cho (Các cột cần phải là duy nhất)

Từ máy chủ sql nhấp chuột phải vào bảng thiết kế chọn Indexes / Keys

Chọn (các) cột sẽ không trùng lặp, sau đó nhập Khóa duy nhất


1

Hơi lạc đề một chút, nhưng nếu bạn muốn di chuyển dữ liệu sang một bảng mới và các bản sao có thể có trong bảng gốc và cột có thể trùng lặp không phải là id, thì a GROUP BYsẽ làm như sau:

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

-1

Một đơn giản DELETEtrước khi INSERTđủ:

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

Switching Table1cho Table2tùy thuộc vào bảng của Idnameghép nối bạn muốn giữ.


3
Xin đừng làm điều này. Về cơ bản, bạn đang nói "bất kỳ dữ liệu nào tôi có đều vô giá trị, chúng ta hãy chèn dữ liệu mới này vào!"
Andir

@Andir Nếu vì lý do nào đó "Table2" không bị rớt sau "INSERT" thì hãy sử dụng các phương pháp khác, nhưng đây là một cách hoàn toàn hợp lệ để đạt được những gì OP yêu cầu.
Sacro

1
Hợp lệ, nhưng chắc chắn chậm hơn và có khả năng bị hỏng nếu không có giao dịch. Nếu bạn đi theo con đường này, hãy kết thúc trong một TRANSaction.
MC9000
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.