PostgreSQL 'NOT IN' และแบบสอบถามย่อย


91

ฉันพยายามดำเนินการค้นหานี้:

SELECT mac, creation_date 
FROM logs 
WHERE logs_type_id=11
AND mac NOT IN (select consols.mac from consols)

แต่ฉันไม่ได้รับผลลัพธ์ ฉันทดสอบแล้วและฉันรู้ว่ามีบางอย่างผิดปกติกับไวยากรณ์ ใน MySQL แบบสอบถามดังกล่าวทำงานได้อย่างสมบูรณ์ ฉันได้เพิ่มแถวเพื่อให้แน่ใจว่ามีแถวmacที่ไม่มีอยู่ในconsolsตาราง แต่ก็ยังไม่ให้ผลลัพธ์ใด ๆ


4
เป็นconsols.macคอลัมน์NULLหรือNOT NULL?
Mark Byers

คำตอบ:


169

เมื่อใช้ NOT IN คุณควรตรวจสอบให้แน่ใจว่าไม่มีค่าใดเป็นโมฆะ:

SELECT mac, creation_date 
FROM logs 
WHERE logs_type_id=11
AND mac NOT IN (
    SELECT mac
    FROM consols
    WHERE mac IS NOT NULL -- add this
)

4
หมายเหตุ: WHERE mac IS NOT NULLไม่จำเป็นต้องใช้อนุประโยคในเคียวรีย่อยเนื่องจากIn(...)จะลบ NULL ออกเสมอ (และรายการที่ซ้ำกัน) เนื่องจากชุดไม่สามารถมี NULL ได้
wildplasser

7
@wildplasser ฉันไม่รู้เกี่ยวกับเรื่องนั้น มันไม่ได้ผลสำหรับฉันจนกว่าฉันจะเพิ่มไฟล์IS NOT NULL. การซ้อนกันSELECTกำลังส่งคืนไม่กี่รายการNULLSและนั่นก็ทำให้ไฟล์IN(SELECT...).
robins35

2
ฉันขอขอบคุณเป็นอย่างยิ่งกับคำอธิบายว่าเหตุใดจึงได้IS NOT NULLผล
mbarkhau

7
ดูเหมือนว่าการใช้NULLในNOT INประโยคจะไม่ได้ผลเนื่องจากการเปรียบเทียบNULLไม่เป็นจริงหรือเท็จ sqlbadpractices.com/using-not-in-operator-with-null-values
mbarkhau

2
คิวรีจะไม่ส่งคืนแถวใด ๆ หากไม่มีis not nullหากเคียวรีย่อยไม่สร้างค่าที่ตรงกันและอย่างน้อยหนึ่งnullค่า จากส่วน 9.22ของคู่มือ PostgreSQL ปัจจุบัน (เวอร์ชัน 10): "[…] หากไม่มีค่าทางขวามือเท่ากันและแถวขวามืออย่างน้อยหนึ่งแถวให้ค่า null ผลลัพธ์ของการสร้าง NOT IN จะเป็นโมฆะไม่เป็นความจริง .”
Christopher Lewis

29

เมื่อใช้ NOT IN คุณควรพิจารณา NOT EXISTS ซึ่งจัดการกรณีว่างเปล่า ดูเพิ่มเติมที่PostgreSQL Wiki

SELECT mac, creation_date 
FROM logs lo
WHERE logs_type_id=11
AND NOT EXISTS (
  SELECT *
  FROM consols nx
  WHERE nx.mac = lo.mac
  );

3
โปรดสังเกตการสูญเสียประสิทธิภาพอย่างมากเมื่อใช้NOT EXISTSvs... NOT IN
IcanDivideBy0

1
@ IcanDivideBy0 ในกรณีส่วนใหญ่พวกเขาสร้างแผนการสืบค้นเดียวกัน คุณได้ทดสอบหรือไม่?
wildplasser

1
นอกจากนี้ยังมีการเพิ่มประสิทธิภาพในการใช้ NOT IN แทน NOT EXISTS ในกรณีของการสืบค้นย่อย ดูwiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_NOT_IN
Gerbrand

8

คุณยังสามารถใช้เงื่อนไข LEFT JOIN และ IS NULL:

SELECT 
  mac, 
  creation_date 
FROM 
  logs
    LEFT JOIN consols ON logs.mac = consols.mac
WHERE 
  logs_type_id=11
AND
  consols.mac IS NULL;

ดัชนีในคอลัมน์ "mac" อาจปรับปรุงประสิทธิภาพ

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