แบบสอบถาม MySQL "ไม่ได้อยู่ใน"


181

ฉันต้องการเรียกใช้แบบสอบถามอย่างง่าย ๆ เพื่อแสดงแถวทั้งหมดTable1ที่ค่าคอลัมน์หลักไม่ปรากฏในคอลัมน์ในตารางอื่น ( Table2)

ฉันพยายามใช้:

SELECT * FROM Table1 WHERE Table1.principal NOT IN Table2.principal

นี่เป็นการโยนข้อผิดพลาดทางไวยากรณ์แทน การค้นหาโดย Google ทำให้ฉันเข้าสู่ฟอรัมที่มีคนบอกว่า MySQL ไม่รองรับNOT INและจำเป็นต้องใช้บางสิ่งที่ซับซ้อนเป็นพิเศษ มันเป็นเรื่องจริงเหรอ? หรือฉันกำลังทำผิดพลาดที่น่ากลัว?


1
และถ้าฉันต้องการข้อมูลที่คล้ายกันจากสามตาราง ฉันหมายถึงหนึ่ง table1 มี 2,000 รายการส่วนอีกสองตารางที่ 2 และ 3 แต่ละรายการมี 500 รายการทั้งหมดนั้นมีชื่อทั่วไปของฟิลด์ เราจะรับรายละเอียดทั้งหมดจากตารางที่ 1 ซึ่งไม่ปรากฏในตารางที่ 2 และ 3 ตาม 'ชื่อ' ได้อย่างไร เราสามารถใช้ NOT IN สองครั้งได้หรือไม่?

คำตอบ:


310

ในการใช้ IN คุณจะต้องมีชุดใช้ไวยากรณ์นี้แทน:

SELECT * FROM Table1 WHERE Table1.principal NOT IN (SELECT principal FROM table2)

85
ระมัดระวังเมื่อสามารถtable2.principal NULLในกรณีที่ว่าNOT INจะกลับมาเสมอFALSEเพราะNOT INจะถือว่าเป็น<> ALLที่เปรียบเทียบแถวทั้งหมดจากแบบสอบถามย่อยเช่นTable1.principal <> table2.principalซึ่งล้มเหลวเมื่อเปรียบเทียบกับNULL: จะไม่ส่งผลTable1.principal <> NULL TRUEวิธีแก้ไข: NOT IN (SELECT principal FROM table2 WHERE principal IS NOT NULL).
Basti

4
ขอบคุณสำหรับความคิดเห็น @Basti! ใช้เวลามากมายในการพยายามทำความเข้าใจว่าเพราะเหตุใดแบบสอบถามจึงไม่ทำงานตามที่คาดไว้
gvas

3
อย่าลืมหลีกเลี่ยงการใช้ 'เลือก *' ในรายการ 'ไม่ได้อยู่ใน' คุณต้องเลือกคอลัมน์ใดคอลัมน์หนึ่ง มิฉะนั้นคุณจะได้รับข้อผิดพลาดนี้: stackoverflow.com/questions/14046838/…
Lorien Brune

165

ตัวเลือกแบบสอบถามย่อยได้รับคำตอบแล้ว แต่โปรดทราบว่าในหลาย ๆ กรณี a LEFT JOINอาจเป็นวิธีที่รวดเร็วกว่าในการทำสิ่งนี้:

SELECT table1.*
FROM table1 LEFT JOIN table2 ON table2.principal=table1.principal
WHERE table2.principal IS NULL

หากคุณต้องการตรวจสอบหลายตารางเพื่อให้แน่ใจว่าไม่มีอยู่ในตารางใด ๆ (เช่นในความคิดเห็นของ SRKR) คุณสามารถใช้สิ่งนี้:

SELECT table1.*
FROM table1
LEFT JOIN table2 ON table2.name=table1.name
LEFT JOIN table3 ON table3.name=table1.name
WHERE table2.name IS NULL AND table3.name IS NULL

2
ในการทดสอบของตัวเองมีประสิทธิภาพการทำงานที่เหมือนกันทั้งงานNOT IN LEFT JOIN+1 ทั้งคู่
BufferStack

เมื่อเคียวรีรันครั้งเดียวคุณควรได้ผลลัพธ์เดียวกันไม่ว่าจะเกิดจากแคชฐานข้อมูลภายใน
Toote

สำหรับฉันแล้วการแสดงนั้นดีกว่ามาก ฉันวิ่งผ่านตารางที่แตกต่างกันในขณะที่พวกเขามีชุดกุญแจต่างประเทศ
Keenora Fluffball

36

ไม่ได้อยู่ในและไม่มีอยู่กับซ้ายเข้าร่วม / เป็นโมฆะใน MySQL

MySQL รวมถึงระบบอื่น ๆ ทั้งหมดยกเว้น SQL Server สามารถปรับให้เหมาะสมLEFT JOIN/IS NULLส่งคืนได้FALSEทันทีที่พบค่าการจับคู่และเป็นระบบเดียวที่ใส่ใจกับเอกสารพฤติกรรมนี้ […] เนื่องจาก MySQL ไม่สามารถใช้HASHและMERGEเข้าร่วมอัลกอริทึมได้สิ่งเดียวที่ANTI JOINสามารถทำได้คือNESTED LOOPS ANTI JOIN

[ ... ]

โดยพื้นฐานแล้ว [ NOT IN]เป็นสิ่งเดียวกันกับที่แผนLEFT JOIN/ การใช้งานแม้จะมีความจริงที่แผนเหล่านี้จะดำเนินการโดยสาขาที่แตกต่างกันของรหัสและพวกเขามีลักษณะที่แตกต่างกันในผลของIS NULL EXPLAINอัลกอริทึมนั้นในความเป็นจริงเดียวกันและแบบสอบถามเสร็จสมบูรณ์ในเวลาเดียวกัน

[ ... ]

มันยากที่จะบอกเหตุผลที่แน่นอน [การลดลงของประสิทธิภาพเมื่อใช้NOT EXISTS]เนื่องจากการปล่อยนี้เป็นแบบเส้นตรงและดูเหมือนจะไม่ขึ้นอยู่กับการแจกแจงข้อมูลจำนวนค่าในทั้งสองตารางเป็นต้นตราบใดที่ทั้งสองฟิลด์ถูกทำดัชนี เนื่องจากมีโค้ดสามชิ้นใน MySQL ที่จำเป็นในการทำงานหนึ่งจึงเป็นไปได้ที่โค้ดที่รับผิดชอบEXISTSในการตรวจสอบพิเศษบางประเภทซึ่งต้องใช้เวลาเพิ่ม

[ ... ]

MySQL สามารถปรับทั้งสามวิธีให้NESTED LOOPS ANTI JOINเหมาะสม […] อย่างไรก็ตามวิธีการทั้งสามนี้สร้างแผนแตกต่างกันสามแผนซึ่งดำเนินการโดยโค้ดสามส่วน รหัสที่ดำเนินการเพรดิเคตEXISTSนั้นมีประสิทธิภาพลดลงประมาณ 30% […]

นั่นเป็นเหตุผลที่วิธีที่ดีที่สุดในการค้นหาค่าที่ขาดหายไปใน MySQL คือการใช้LEFT JOIN/IS NULLหรือมากกว่าNOT INNOT EXISTS

(เน้นเพิ่ม)


7

น่าเสียดายที่ดูเหมือนว่าจะมีปัญหากับการใช้ MySql ของประโยค "NOT IN" การถ่ายภาพหน้าจอด้านล่างแสดงตัวเลือกแบบสอบถามย่อยที่ส่งคืนผลลัพธ์ที่ผิด:

mysql> show variables like '%version%';
+-------------------------+------------------------------+
| Variable_name           | Value                        |
+-------------------------+------------------------------+
| innodb_version          | 1.1.8                        |
| protocol_version        | 10                           |
| slave_type_conversions  |                              |
| version                 | 5.5.21                       |
| version_comment         | MySQL Community Server (GPL) |
| version_compile_machine | x86_64                       |
| version_compile_os      | Linux                        |
+-------------------------+------------------------------+
7 rows in set (0.07 sec)

mysql> select count(*) from TABLE_A where TABLE_A.Pkey not in (select distinct TABLE_B.Fkey from TABLE_B );
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.07 sec)

mysql> select count(*) from TABLE_A left join TABLE_B on TABLE_A.Pkey = TABLE_B.Fkey where TABLE_B.Pkey is null;
+----------+
| count(*) |
+----------+
|      139 |
+----------+
1 row in set (0.06 sec)

mysql> select count(*) from TABLE_A where NOT EXISTS (select * FROM TABLE_B WHERE TABLE_B.Fkey = TABLE_A.Pkey );
+----------+
| count(*) |
+----------+
|      139 |
+----------+
1 row in set (0.06 sec)

mysql> 

7

ระวังNOT INไม่ได้เป็นนามแฝงสำหรับ<> ANYแต่สำหรับ<> ALL!

http://dev.mysql.com/doc/refman/5.0/en/any-in-some-subqueries.html

SELECT c FROM t1 LEFT JOIN t2 USING (c) WHERE t2.c IS NULL

ไม่สามารถถูกแทนที่ด้วย

SELECT c FROM t1 WHERE c NOT IN (SELECT c FROM t2)

คุณต้องใช้

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