เลือกแถวที่ไม่ปรากฏในตารางอื่น


172

ฉันมีตาราง postgresql สองตาราง:

table name     column names
-----------    ------------------------
login_log      ip | etc.
ip_location    ip | location | hostname | etc.

ฉันต้องการที่จะได้รับทุกที่อยู่ IP จากซึ่งไม่ได้มีแถวในlogin_log ฉันลองใช้แบบสอบถามนี้ แต่มีข้อผิดพลาดทางไวยากรณ์เกิดขึ้นip_location

SELECT login_log.ip 
FROM login_log 
WHERE NOT EXIST (SELECT ip_location.ip
                 FROM ip_location
                 WHERE login_log.ip = ip_location.ip)
ERROR: syntax error at or near "SELECT"
LINE 3: WHERE NOT EXIST (SELECT ip_location.ip`

ฉันยังสงสัยว่าแบบสอบถามนี้ (ด้วยการปรับเพื่อให้ทำงานได้) เป็นแบบสอบถามที่มีประสิทธิภาพดีที่สุดสำหรับวัตถุประสงค์นี้

คำตอบ:


386

มีเทคนิคพื้นฐาน 4 ประการสำหรับงานนี้ซึ่งทั้งหมดเป็น SQL มาตรฐาน

NOT EXISTS

มักจะเร็วที่สุดใน Postgres

SELECT ip 
FROM   login_log l 
WHERE  NOT EXISTS (
   SELECT  -- SELECT list mostly irrelevant; can just be empty in Postgres
   FROM   ip_location
   WHERE  ip = l.ip
   );

พิจารณาด้วย:

LEFT JOIN / IS NULL

บางครั้งมันเร็วที่สุด มักจะสั้นที่สุด NOT EXISTSมักจะส่งผลในแผนแบบสอบถามเดียวกับ

SELECT l.ip 
FROM   login_log l 
LEFT   JOIN ip_location i USING (ip)  -- short for: ON i.ip = l.ip
WHERE  i.ip IS NULL;

EXCEPT

สั้น. ไม่สามารถรวมเข้ากับการสืบค้นที่ซับซ้อนได้ง่ายขึ้น

SELECT ip 
FROM   login_log

EXCEPT ALL  -- "ALL" keeps duplicates and makes it faster
SELECT ip
FROM   ip_location;

โปรดทราบว่า ( ต่อเอกสารประกอบ ):

รายการที่ซ้ำกันจะถูกกำจัดเว้นแต่EXCEPT ALLจะใช้

โดยทั่วไปคุณจะต้องการALLคำหลัก หากคุณไม่สนใจยังคงใช้มันเพราะมันทำให้การค้นหาได้เร็วขึ้น

NOT IN

เท่านั้นที่ดีไม่มีNULLค่าหรือถ้าคุณรู้ว่าจะจัดการNULLอย่างถูกต้อง ฉันจะไม่ใช้มันเพื่อจุดประสงค์นี้ นอกจากนี้ประสิทธิภาพอาจลดลงเมื่อใช้ตารางที่ใหญ่กว่า

SELECT ip 
FROM   login_log
WHERE  ip NOT IN (
   SELECT DISTINCT ip  -- DISTINCT is optional
   FROM   ip_location
   );

NOT INดำเนินการ "กับดัก" สำหรับNULLค่าที่ด้านใดด้านหนึ่ง:

คำถามที่คล้ายกันใน dba.SE กำหนดเป้าหมายที่ MySQL:


2
SQL ชนิดใดที่จะทำงานได้เร็วขึ้นโดยพิจารณาจากปริมาณข้อมูลสูงในทั้งสองตาราง (สมมติเป็นพันล้าน)
Teja

ยกเว้นทั้งหมดเร็วที่สุดสำหรับฉัน
Dan Parker

ระวังด้วยLEFT JOIN- หากมีแถวที่ตรงกันหลายแถวในตารางค้นหาสิ่งนี้จะสร้างรายการที่ซ้ำกันในแบบสอบถามหลักของคุณสำหรับแต่ละแถวที่ตรงกันซึ่งอาจไม่ต้องการ
Matthias Fripp

@MatthiasFripp: ยกเว้นว่าสิ่งนี้ไม่สามารถเกิดขึ้นได้WHERE i.ip IS NULLหมายความว่าไม่มีการแข่งขันเลย
Erwin Brandstetter

@ erwin-brandstetter: จุดดี ฉันเพิ่มความคิดเกี่ยวกับความเป็นไปได้ของการแข่งขันที่เป็นไปได้หลายทาง
Matthias Fripp

2

A. ) คำสั่งไม่ใช่ EXISTS คุณไม่มี 'S'

B. ) ใช้ NOT IN แทน

SELECT ip 
  FROM login_log 
  WHERE ip NOT IN (
    SELECT ip
    FROM ip_location
  )
;

4
ไม่ได้อยู่ในชุดข้อมูลขนาดใหญ่เป็นความคิดที่น่ากลัว ช้ามาก มันไม่ดีและควรหลีกเลี่ยง
Grzegorz Grabek

0

SELECT * FROM testcases1 t WHERE NOT EXISTS ( SELECT 1
FROM executions1 i WHERE t.tc_id = i.tc_id and t.pro_id=i.pro_id and pro_id=7 and version_id=5 ) and pro_id=7 ;

ที่นี่ตาราง testcases1 มีข้อมูลทั้งหมดและตาราง executions1 มีข้อมูลบางส่วนในตาราง testcases1 ฉันดึงเฉพาะข้อมูลที่ไม่มีอยู่ในตาราง exections1 (และแม้ว่าฉันจะให้เงื่อนไขบางอย่างที่คุณสามารถให้ได้) ระบุเงื่อนไขที่ไม่ควรมีในการดึงข้อมูลควรอยู่ในวงเล็บ


0

สิ่งนี้สามารถลอง ...

SELECT l.ip, tbl2.ip as ip2, tbl2.hostname
FROM   login_log l 
LEFT   JOIN (SELECT ip_location.ip, ip_location.hostname
             FROM ip_location
             WHERE ip_location.ip is null)tbl2

2
WHERE ip_location.ip is null- WHEREเงื่อนไขจะเป็นจริงได้อย่างไร? นอกจากนี้แบบสอบถามย่อยไม่ได้สัมพันธ์กัน
Istiaque Ahmed
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.