หลีกเลี่ยงรายการที่ซ้ำกันใน INSERT INTO SELECT query ใน SQL Server


109

ฉันมีสองตารางต่อไปนี้:

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

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

ฉันต้องใส่ข้อมูลจากการTable1 Table2ฉันสามารถใช้ไวยากรณ์ต่อไปนี้:

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

อย่างไรก็ตามในกรณีของฉัน ID ที่ซ้ำกันอาจมีอยู่ในTable2(ในกรณีของฉันมันเป็นแค่ " 1") และฉันไม่ต้องการคัดลอกอีกครั้งเพราะจะทำให้เกิดข้อผิดพลาด

ฉันสามารถเขียนสิ่งนี้:

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

มีวิธีที่ดีกว่านี้โดยไม่ใช้IF - ELSEหรือไม่? ฉันต้องการหลีกเลี่ยงสองINSERT INTO-SELECTคำสั่งตามเงื่อนไขบางประการ

คำตอบ:


204

ใช้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)

ใช้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)

ใช้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

ในสามตัวเลือกLEFT JOIN/IS NULLนี้มีประสิทธิภาพน้อยกว่า ดูที่ลิงค์นี้สำหรับรายละเอียดเพิ่มเติม


9
เพียงแค่ชี้แจงเกี่ยวกับเวอร์ชัน NOT EXISTS คุณจะต้องมีคำใบ้ด้วย (HOLDLOCK) ไม่เช่นนั้นจะไม่มีการล็อก (เนื่องจากไม่มีแถวให้ล็อก!) เพื่อให้เธรดอื่นสามารถแทรกแถวใต้ตัวคุณได้
IDisposable

3
น่าสนใจเพราะฉันเชื่อเสมอว่าการเข้าร่วมเร็วกว่าการเลือกย่อย บางทีอาจมีไว้สำหรับการรวมแบบตรงเท่านั้นและไม่สามารถใช้ได้กับการรวมด้านซ้าย
Duncan

1
Duncan การเข้าร่วมมักจะเร็วกว่าการเลือกย่อยเมื่อเป็นแบบสอบถามย่อยที่สัมพันธ์กัน หากคุณมีแบบสอบถามย่อยในรายการเลือกการเข้าร่วมมักจะเร็วกว่า
HLGEM

9
NOT EXISTSมีประโยชน์อย่างยิ่งกับคีย์หลักแบบผสมNOT INจะใช้ไม่ได้
tomash

1
@OMGPonies ลิงก์ของคุณสำหรับรายละเอียดเพิ่มเติมดูเหมือนจะตายไปแล้ว คุณมีสิ่งอื่นที่อาจเป็นประโยชน์หรือไม่?
FreeMan

36

ใน MySQL คุณสามารถทำได้:

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

SQL Server มีอะไรที่คล้ายกันหรือไม่?


5
+1 สำหรับการให้ความรู้ฉันในเรื่องนี้ ไวยากรณ์ที่ดีมาก สั้นกว่าและดีกว่าที่เคยใช้แน่นอน น่าเสียดายที่เซิร์ฟเวอร์ Sql ไม่มีสิ่งนี้
Ashish Gupta

13
ไม่เป็นความจริงทั้งหมด เมื่อคุณสร้างดัชนีเฉพาะคุณสามารถตั้งค่าเป็น "ละเว้นรายการที่ซ้ำกัน" ซึ่งในกรณีนี้ SQL Server จะละเว้นความพยายามใด ๆ ในการเพิ่มรายการที่ซ้ำกัน
IamIC

2
และ SQL Server ยังไม่สามารถ ...
Smack Jack

1
SQL Server ยังคงทำไม่ได้?
Ingus

8

ฉันเพิ่งมีปัญหาที่คล้ายกันคำหลัก DISTINCT ใช้งานได้อย่างมหัศจรรย์:

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

21
เว้นแต่ฉันทั้งหมดเข้าใจผิดคุณนี้จะทำงานหากคุณมีรายการที่ซ้ำกันในชุดที่คุณใส่จาก อย่างไรก็ตามจะไม่ช่วยหากชุดที่คุณกำลังแทรกอาจซ้ำกับข้อมูลที่มีอยู่แล้วในinsert intoตาราง
FreeMan

5

เมื่อเร็ว ๆ นี้ฉันประสบปัญหาเดียวกัน ...
นี่คือสิ่งที่ใช้ได้ผลสำหรับฉันใน MS SQL server 2017 ...
คีย์หลักควรตั้งค่าบน ID ในตารางที่ 2 ...
คุณสมบัติของคอลัมน์และคอลัมน์ควรเหมือนกันแน่นอนระหว่างทั้งสอง ตาราง สิ่งนี้จะใช้ได้ในครั้งแรกที่คุณเรียกใช้สคริปต์ด้านล่าง รหัสที่ซ้ำกันในตารางที่ 1 จะไม่แทรก ...

หากคุณเรียกใช้ครั้งที่สองคุณจะได้รับไฟล์

การละเมิดข้อผิดพลาดข้อ จำกัด ของคีย์หลัก

นี่คือรหัส:

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

4

การใช้ignore Duplicatesดัชนีเฉพาะตามที่แนะนำโดย IanC นี่คือวิธีแก้ปัญหาของฉันสำหรับปัญหาที่คล้ายกันการสร้างดัชนีด้วยตัวเลือกWITH IGNORE_DUP_KEY

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

อ้างอิง: index_option


4

จาก SQL Server คุณสามารถตั้งค่าดัชนีคีย์เฉพาะบนตารางสำหรับ (คอลัมน์ที่ต้องไม่ซ้ำกัน)

จากเซิร์ฟเวอร์ sql คลิกขวาที่การออกแบบตารางเลือก Indexes / Keys

เลือกคอลัมน์ที่จะไม่ซ้ำกันจากนั้นพิมพ์คีย์เฉพาะ


1

นอกเรื่องเล็กน้อย แต่ถ้าคุณต้องการย้ายข้อมูลไปยังตารางใหม่และรายการที่ซ้ำกันที่เป็นไปได้จะอยู่ในตารางเดิมและคอลัมน์ที่อาจซ้ำกันไม่ใช่รหัสGROUP BYจะทำ:

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

-1

ง่ายๆDELETEก่อนที่INSERTจะพอเพียง:

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

Switching Table1สำหรับTable2ซึ่งขึ้นอยู่กับตารางIdและnameการจับคู่ที่คุณต้องการที่จะรักษา


3
กรุณาอย่าทำเช่นนี้ โดยพื้นฐานแล้วคุณกำลังพูดว่า "ข้อมูลใดก็ตามที่ฉันมีก็ไร้ค่าเรามาแทรกข้อมูลใหม่นี้กันเถอะ!"
Andir

@Andir หากด้วยเหตุผลบางประการ "Table2" ไม่ควรหลุดหลังจาก "INSERT" ให้ใช้วิธีการอื่น แต่นี่เป็นวิธีที่ถูกต้องอย่างสมบูรณ์ในการบรรลุสิ่งที่ OP ขอ
Sacro

1
ถูกต้อง แต่ช้ากว่าและอาจเสียหายได้โดยไม่ต้องทำธุรกรรม ถ้าคุณไปเส้นทางนี้ให้ห่อด้วย TRANSaction
MC9000
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.