จะเลือกแถวที่ไม่มีรายการที่ตรงกันในตารางอื่นได้อย่างไร


323

ฉันทำงานบำรุงรักษาบางอย่างในแอปพลิเคชันฐานข้อมูลและฉันค้นพบว่าความสุขของความสุขถึงแม้ว่าค่าจากตารางหนึ่งจะถูกใช้ในรูปแบบของกุญแจต่างประเทศไม่มีข้อ จำกัด ของรหัสต่างประเทศในตาราง

ฉันพยายามเพิ่มข้อ จำกัด FK ในคอลัมน์เหล่านี้ แต่ฉันพบว่าเนื่องจากมีการโหลดข้อมูลที่ไม่ดีทั้งหมดในตารางจากข้อผิดพลาดก่อนหน้าซึ่งได้รับการแก้ไขอย่างไร้เดียงสาฉันต้องค้นหาแถวที่ไม่ จับคู่กับตารางอื่นแล้วลบออก

ฉันพบตัวอย่างแบบสอบถามประเภทนี้บนเว็บ แต่พวกเขาดูเหมือนจะให้ตัวอย่างมากกว่าคำอธิบายและฉันไม่เข้าใจว่าทำไมจึงทำงาน

บางคนสามารถอธิบายให้ฉันรู้วิธีสร้างคิวรีที่ส่งคืนแถวทั้งหมดโดยไม่มีข้อมูลตรงกันในตารางอื่นและสิ่งที่ทำอยู่เพื่อให้ฉันสามารถสร้างคิวรีเหล่านี้ด้วยตัวเองแทนที่จะมาทำงานกับ SO สำหรับทุกตารางในระเบียบนี้ ไม่มีข้อ จำกัด FK?

คำตอบ:


614

นี่เป็นคำถามง่ายๆ:

SELECT t1.ID
FROM Table1 t1
    LEFT JOIN Table2 t2 ON t1.ID = t2.ID
WHERE t2.ID IS NULL

ประเด็นสำคัญคือ:

  1. LEFT JOINถูกนำมาใช้; นี้จะกลับแถวทั้งหมดจากโดยไม่คำนึงถึงหรือไม่ว่ามีความเป็นแถวที่ตรงกันในTable1Table2

  2. WHERE t2.ID IS NULLข้อ; สิ่งนี้จะ จำกัด ผลลัพธ์ที่ส่งกลับไปยังแถวเหล่านั้นเท่านั้นที่ ID ที่ส่งคืนจากTable2นั้นเป็นโมฆะ - หรืออีกนัยหนึ่งไม่มีการบันทึกTable2สำหรับ ID Table1นั้น ๆ Table2.IDจะกลับมาเป็นโมฆะสำหรับระเบียนทั้งหมดจากการที่ประชาชนไม่ตรงกับในTable1Table2


4
ล้มเหลวถ้า ID เป็น NULL
Michael

169
@Michael - หากมีNULLID ที่ถูกต้องในสคีมาของคุณคุณอาจมีปัญหามากขึ้นคุณจะไม่เห็นด้วยไหม :)
rinogo

1
จะใช้งานได้แม้ว่า table1 จะมีการบันทึกมากกว่านั้น table2? หาก table1 มี 100 รายการและ table2 มี 200 รายการ (100 รายการที่ตรงกัน / เข้าร่วมและ 100 ที่ไม่ตรงกัน / เข้าร่วม) เราจะได้รับ 200 ระเบียนคืนหรือไม่
Juan Velez

1
ฉันมักจะล้อมรอบด้านซ้ายเข้าร่วมเป็นมุมมองย่อย / อินไลน์เพื่อให้แน่ใจว่าไม่มีการมีส่วนร่วมระหว่าง WHERE clause และ LEFT JOIN
Andrew Wolfe

1
@Jas จุดสำคัญ 1 ของคำตอบทุกแถวจากตารางแรกแม้จะไม่ตรงกับ t1.ID = เงื่อนไข t2.ID ของการเข้าร่วมที่เหลือ หากคุณเปลี่ยนบรรทัดแรกเป็นSELECT t1.ID, t2.IDและลบบรรทัดที่คุณจะได้รับความคิดที่ดีขึ้นวิธีการทำงาน
Peter Laboš

97

ฉันจะใช้EXISTSการแสดงออกเนื่องจากมันมีประสิทธิภาพมากขึ้นคุณสามารถเลือกแถวที่คุณต้องการเข้าร่วมได้แม่นยำยิ่งขึ้นในกรณีที่LEFT JOINคุณต้องใช้ทุกอย่างที่อยู่ในตารางที่เข้าร่วม ประสิทธิภาพของมันอาจจะเหมือนกับในกรณีที่LEFT JOINมีการทดสอบว่าง

SELECT t1.ID
FROM Table1 t1
WHERE NOT EXISTS (SELECT t2.ID FROM Table2 t2 WHERE t1.ID = t2.ID)

บางสิ่งบางอย่างง่าย ๆ นี้ได้รับการจัดการโดยเครื่องมือเพิ่มประสิทธิภาพข้อความค้นหาเพื่อการดำเนินการที่ดีที่สุด
Andrew Wolfe

2
ใช่ข้อได้เปรียบหลักของEXISTSมันคือความแปรปรวน
Ondrej Bozek

1
ง่ายสง่างามและแก้ไขปัญหาของฉันได้! ทำได้ดีนี่!
MikeMighty

2
จริง ๆ แล้วลดความเร็วของหนึ่งแบบสอบถามที่ฉันมีจาก 7 วินาทีถึง 200ms ... (เทียบกับWHERE t2.id IS NULL) ขอบคุณ
Moti Korets

4
@MotiKorets คุณหมายถึงการเพิ่มความเร็ว :)
Ondrej Bozek

14
SELECT id FROM table1 WHERE foreign_key_id_column NOT IN (SELECT id FROM table2)

ตารางที่ 1 มีคอลัมน์ที่คุณต้องการเพิ่มข้อ จำกัด คีย์ต่างประเทศ แต่ค่าในค่าforeign_key_id_columnทั้งหมดที่ไม่ตรงกับidในตารางที่ 2

  1. การเลือกเริ่มต้นแสดงรายการids จาก table1 นี่คือแถวที่เราต้องการลบ
  2. NOT INประโยคหนึ่งในงบที่ จำกัด แบบสอบถามไปยังแถวเดียวที่คุ้มค่าในการที่foreign_key_id_columnไม่ได้อยู่ในรายชื่อของตารางที่ 2 ids
  3. SELECTคำสั่งในวงเล็บจะได้รับรายชื่อทั้งหมดidของที่อยู่ในตารางที่ 2

@ zb226: ลิงก์ของคุณเกี่ยวกับข้อ จำกัด ในINอนุประโยคที่มีรายการค่าตัวอักษร มันใช้ไม่ได้กับการใช้INข้อกับผลลัพธ์ของแบบสอบถามย่อย คำตอบที่ยอมรับสำหรับคำถามนั้นแก้ปัญหาได้จริงโดยใช้แบบสอบถามย่อย (รายการค่าตัวอักษรขนาดใหญ่เป็นปัญหาเนื่องจากสร้างนิพจน์ SQL ขนาดใหญ่แบบสอบถามย่อยทำงานได้ดีเนื่องจากแม้ว่ารายการผลลัพธ์จะมีขนาดใหญ่นิพจน์ SQL เองก็จะเล็ก)
Kannan Goundan

@ KannanGoundan คุณพูดถูก ถอนความคิดเห็นที่มีข้อบกพร่อง
zb226

8

ในกรณีที่T2เป็นตารางที่คุณกำลังเพิ่มข้อ จำกัด :

SELECT *
FROM T2
WHERE constrained_field NOT
IN (
    SELECT DISTINCT t.constrained_field
    FROM T2 
    INNER JOIN T1 t
    USING ( constrained_field )
)

และลบผลลัพธ์


4

ให้เรามี 2 ตารางต่อไปนี้ (เงินเดือนและพนักงาน) ป้อนคำอธิบายรูปภาพที่นี่

ตอนนี้ฉันต้องการบันทึกจากตารางพนักงานที่ไม่ได้อยู่ในเงินเดือน เราสามารถทำได้ 3 วิธี:

  1. ใช้การเข้าร่วมภายใน
select * from employee
where id not in(select e.id from employee e inner join salary s on e.id=s.id)

ป้อนคำอธิบายรูปภาพที่นี่

  1. ใช้การรวมภายนอกด้านซ้าย
select * from employee e 
left outer join salary s on e.id=s.id  where s.id is null

ป้อนคำอธิบายรูปภาพที่นี่

  1. ใช้การเข้าร่วมแบบเต็ม
select * from employee e
full outer join salary s on e.id=s.id where e.id not in(select id from salary)

ป้อนคำอธิบายรูปภาพที่นี่


2

จากคำถามที่คล้ายกันที่นี่MySQL Inner เข้าร่วมแบบสอบถามเพื่อรับบันทึกไม่ปรากฏในตารางอื่นฉันได้รับการทำงาน

SELECT * FROM bigtable 
LEFT JOIN smalltable ON bigtable.id = smalltable.id 
WHERE smalltable.id IS NULL

smalltableเป็นที่ที่คุณมีบันทึกที่หายไปbigtableเป็นที่ที่คุณมีบันทึกทั้งหมด รายการของแบบสอบถามทั้งหมดระเบียนที่ไม่ได้อยู่ในแต่อยู่บนsmalltable bigtableคุณสามารถแทนที่idด้วยเกณฑ์การจับคู่อื่น ๆ


0

คุณสามารถเลือกดูได้ตามที่แสดงด้านล่าง:

CREATE VIEW AuthorizedUserProjectView AS select t1.username as username, t1.email as useremail, p.id as projectid, 
(select m.role from userproject m where m.projectid = p.id and m.userid = t1.id) as role 
FROM authorizeduser as t1, project as p

จากนั้นทำงานในมุมมองเพื่อเลือกหรืออัปเดต:

select * from AuthorizedUserProjectView where projectid = 49

ซึ่งให้ผลลัพธ์ตามที่แสดงในภาพด้านล่างเช่นสำหรับคอลัมน์ที่ไม่ได้จับคู่ว่างไว้เต็มแล้ว

[Result of select on the view][1]

0

ฉันไม่ทราบว่าตัวใดที่เหมาะที่สุด (เทียบกับ @AdaTheDev) แต่อันนี้ดูเหมือนจะเร็วกว่าเมื่อฉันใช้ (atleast สำหรับฉัน)

SELECT id  FROM  table_1 EXCEPT SELECT DISTINCT (table1_id) table1_id FROM table_2

หากคุณต้องการรับคุณลักษณะเฉพาะอื่น ๆ คุณสามารถใช้:

SELECT COUNT(*) FROM table_1 where id in (SELECT id  FROM  table_1 EXCEPT SELECT DISTINCT (table1_id) table1_id FROM table_2);


-2

คุณสามารถทำอะไรเช่นนี้

   SELECT IFNULL(`price`.`fPrice`,100) as fPrice,product.ProductId,ProductName 
          FROM `products` left join `price` ON 
          price.ProductId=product.ProductId AND (GeoFancingId=1 OR GeoFancingId 
          IS NULL) WHERE Status="Active" AND Delete="No"

-6

จะเลือกแถวที่ไม่มีรายการที่ตรงกันในตารางทั้งสองได้อย่างไร

    เลือก * จาก [dbo] [EmppDetails] e
     เข้าร่วมขวา [พนักงาน]. [เพศ] d ใน e.Gid = d.Gid
    โดยที่ e.Gid คือ Null

    สหภาพ 
    เลือก * จาก [dbo] [EmppDetails] e
     ออกจากการเข้าร่วม [พนักงาน] [เพศ] d ใน e.Gid = d.Gid
    โดยที่ d.Gid คือ Null

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