SQL JOIN โดยมีเงื่อนไข WHERE เป็นลบ

ฉันมีสองตารางเอกสารและป้ายกำกับ (ไม่ใช่กรณีจริงของฉัน ฉันใช้การเปรียบเทียบ) เอกสารหนึ่งฉบับสามารถมี N Labels ได้ เมื่อฉันต้องการเลือกเอกสารที่มีป้ายกำกับ ฉันสามารถทำสิ่งนี้ได้อย่างง่ายดาย

select D.id from document D
join label L on  D.id = L.document_id
where L.value in('label1','label2',...)

จะเขียนแบบสอบถามที่ฉันต้องการเอกสารที่ไม่มีป้ายกำกับได้อย่างไร เมื่อฉันทำสิ่งนี้

select D.id from document D
join label L on  D.id = L.document_id
where L.value not in('label1','label2',...)

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

จริงๆ แล้วฉันกำลังทำงานกับแบบสอบถามแบบนั้นในแบบสอบถามที่พิมพ์ Java Spring JPA ฉันจำเป็นต้องแก้ไขกรณีนี้สำหรับกรอบงานการกรองของฉัน แต่ฉันคิดว่าควรแก้ไขปัญหานี้ในระดับ SQL ก่อนจะดีกว่า

อย่างไรก็ตาม เราสามารถแทนที่ "not in" ด้วย "!=" เพื่อความเรียบง่ายได้ ปัญหายังคงเหมือนเดิม

มีแนวคิดสำหรับวิธีแก้ปัญหาง่ายๆ หรือไม่? ขอบคุณล่วงหน้า


person Lukas S    schedule 23.03.2020    source แหล่งที่มา


คำตอบ (3)


คุณสามารถทำได้โดยใช้ LEFT JOIN โดยที่คุณเลือกแถวที่ไม่ตรงกันทั้งหมด:

select D.id 
from document D left join label L 
on D.id = L.document_id and L.value in('label1','label2',...)
where L.document_id is null

หรือด้วย NOT EXISTS:

select D.id from document D 
where not exists (
  select 1 from label L
  where L.document_id = D.id and L.value in('label1','label2',...)
)

หรือด้วย NOT IN:

select id from document
where id not in (
  select document_id from label 
  where value in('label1','label2',...)
)

ดูการสาธิต
แบบง่าย

person forpas    schedule 23.03.2020
comment
ฉันคิดว่าตัวเลือก NOT IN เท่านั้นที่ถูกต้อง 2 รายการแรกสามารถส่งคืน ID ที่ไม่ต้องการได้ หนึ่ง ID สามารถมี label1, label2 แต่ยังมี label3 อีกด้วย เนื่องจาก label3 ID จะถูกส่งคืน แต่จริงๆ แล้วไม่ควรส่งคืน - person IMujagic; 24.03.2020
comment
หาก document_id ในตาราง label มีค่าใดๆ ('label1', 'label2',...) ดังนั้น exists จะส่งกลับ true และ not exists จะส่งกลับ false และ id นั้นจะไม่อยู่ในผลลัพธ์ เช่นเดียวกับ LEFT JOIN - person forpas; 24.03.2020
comment
@IMujagic ตรวจสอบลิงก์สาธิตในคำตอบของฉัน - person forpas; 24.03.2020
comment
จริงอยู่ ฉันคอมไพล์ใหม่อีกครั้งแล้ว .. แย่เลย ฉันเข้าใจผิดสองข้อแรก ฉันได้รับอิทธิพลมากเกินไปกับวิธีแก้ปัญหา NOT IN :) - person IMujagic; 24.03.2020
comment
ขอบคุณมาก. ฉันจำเป็นต้องรีเฟรชข้อมูล SQL นี้จริงๆ ทันใดนั้นมันก็ดูชัดเจนมาก - person Lukas S; 24.03.2020
comment
เสร็จแล้ว. ปัญหาสุดท้ายที่ฉันมีคือฉันไม่รู้วิธีการทำเช่นนี้ใน JPA Criteria API แต่มันเป็นหัวข้อที่แตกต่างกัน - person Lukas S; 24.03.2020

หากคุณกำลังมองหาเอกสารที่ไม่มีป้ายกำกับ คุณต้องมีการรวมภายนอก

select D.id 
from document D
left join label L on  D.id = L.document_id
where L.value is null
person Fran Cerezo    schedule 23.03.2020

หากคุณต้องการรับเอกสารที่ไม่มีป้ายกำกับใดๆ คุณสามารถยกเว้นรหัสที่คุณจะได้รับจากการสืบค้นเชิงบวกที่คุณมีอยู่แล้วโดยใช้ not in ในกรณีที่คุณต้องการยกเว้นบางป้ายกำกับในขณะที่ค้นหาป้ายกำกับอื่น คุณสามารถรวมทั้งสองป้ายกำกับได้โดยใช้:

    where id not in (
      select D.id from document D
      join label L on  D.id = L.document_id
      where L.value in('label1', ...)
    ) and id in (
      select D.id from document D
      join label L on  D.id = L.document_id
      where L.value in('label2', ...)
    )
person T. Pieper    schedule 23.03.2020