เหตุใดฉันไม่สามารถใช้ค่า Null ในการเข้าร่วมได้


13

ฉันได้แก้ไขปัญหาการสืบค้นโดยใช้ ... row_number() over (partition by... นี่เป็นคำถามทั่วไปเพิ่มเติมเกี่ยวกับสาเหตุที่เราไม่สามารถใช้คอลัมน์ที่มีค่า Null ในการเข้าร่วมได้ เหตุใดค่าโมฆะจึงไม่เท่ากับค่าว่างเพื่อการเข้าร่วม?

คำตอบ:


31

เหตุใดค่าโมฆะจึงไม่เท่ากับค่าว่างสำหรับการเข้าร่วม?

เพียงบอกให้ Oracle ทำเช่นนั้น:

select *
from one t1 
  join two t2 on coalesce(t1.id, -1) = coalesce(t2.id, -1);

(โปรดทราบว่าใน SQL มาตรฐานคุณสามารถใช้t1.id is not distinct from t2.idเพื่อรับโอเปอเรเตอร์ความปลอดภัยแบบ null-safe แต่ Oracle ไม่รองรับสิ่งนั้น)

แต่วิธีนี้จะใช้ได้เฉพาะในกรณีที่ค่าการแทนที่ (-1 ในตัวอย่างด้านบน) ไม่ปรากฏในตาราง การค้นหาค่า "วิเศษ" สำหรับตัวเลขอาจเป็นไปได้ แต่จะยากมากสำหรับค่าอักขระ (โดยเฉพาะอย่างยิ่งเนื่องจาก Oracle ปฏิบัติต่อสตริงว่างnullเช่นกัน)

บวก: จะไม่มีการใช้ดัชนีในidคอลัมน์ (คุณสามารถกำหนดดัชนีตามฟังก์ชันโดยใช้coalesce()นิพจน์)

ตัวเลือกอื่นที่ใช้ได้กับทุกประเภทโดยไม่มีค่าเวทย์มนตร์:

              on t1.id = t2.id or (t1.id is null and t2.id is null)

แต่คำถามที่แท้จริงคือ: มันสมเหตุสมผลหรือไม่

พิจารณาข้อมูลตัวอย่างต่อไปนี้:

ตารางที่หนึ่ง

id
----
1
2
(null)
(null)

ตารางที่สอง

id
----
1
2
(null)
(null)
(null)

ควรเลือกการรวมกันของค่า Null ใดในการเข้าร่วม ตัวอย่างด้านบนของฉันจะส่งผลให้มีค่า cross สำหรับการเข้าร่วมทั้งหมด

T1_ID  | T2_ID 
-------+-------
     1 |      1
     2 |      2
(null) | (null)
(null) | (null)
(null) | (null)
(null) | (null)
(null) | (null)
(null) | (null)

6

อีกวิธีหนึ่งคุณสามารถทำให้ค่า Null สองค่าจับคู่กันโดยใช้INTERSECTเป็นตัวดำเนินการความเสมอภาค:

SELECT
  *
FROM
  t1
  INNER JOIN t2
    ON EXISTS (SELECT t1.ID FROM DUAL INTERSECT SELECT t2.ID FROM DUAL)
;

ดูตัวอย่าง DBFiddle นี้สำหรับภาพประกอบ

ของหลักสูตรลักษณะนี้ค่อนข้างคำหนึ่งแม้ว่ามันจะเป็นจริงไม่ได้นานกว่าข้อเสนอแนะของ BriteSponge อย่างไรก็ตามมันไม่แน่นอนถ้าคุณให้อภัยคำพิพากษาถึงความรัดกุมของที่กล่าวถึงก่อนหน้านี้ในวิธีการแสดงความคิดเห็นมาตรฐานซึ่งเป็นIS NOT DISTINCT FROMผู้ประกอบการยังไม่ได้รับการสนับสนุนใน Oracle


2

เพื่อความสมบูรณ์ฉันจะพูดถึงว่าSYS_OP_MAP_NONNULLตอนนี้ฟังก์ชั่นสามารถนำมาใช้อย่างปลอดภัยเพื่อเปรียบเทียบค่าที่เป็นโมฆะเพราะตอนนี้มันถูกบันทึกไว้ในเอกสาร 12c ซึ่งหมายความว่า Oracle ไม่เพียง แต่จะลบและทำลายรหัส

SELECT *
FROM   one t1 
       JOIN two t2
         ON SYS_OP_MAP_NONNULL(t1.id) = SYS_OP_MAP_NONNULL(t2.id)

ข้อได้เปรียบที่คุณไม่ได้เจอกับปัญหาเรื่อง 'ความมหัศจรรย์'

การอ้างอิงในเอกสารของ Oracle อยู่ที่มุมมองที่เป็นรูปธรรมพื้นฐาน - การเลือกดัชนีสำหรับมุมมองที่ปรากฏ


ดังนั้นจึงมีการบันทึกไว้ในตอนนี้? เพราะAskTom (ในปี 2003)กล่าวว่า: " - มันไม่มีเอกสารและดังนั้นจึงมีความเสี่ยงที่จะออกไปหรือเปลี่ยนฟังก์ชั่น ซึ่งเพียงพอแล้วที่บอกว่าควรทำให้ผู้คนแค่" หยุดอ่าน "ไปที่นั่นและคุณอาจคลั่งไคล้ในรุ่นถัดไป วิธีที่ถูกต้องเพียงอย่างเดียวคือ: where (a = b or (a is null and b is null)) จุดนั่นคือความคิดของฉันเกี่ยวกับมันฉันจะไม่พิจารณาการใช้sys_op_map_nonnullงานไม่สนใจคนที่อยู่หลังม่าน
ypercubeᵀᴹ

หากคุณมีลิงค์โปรดเพิ่มไปที่คำถาม ฉันไม่พบการพูดถึงในฟังก์ชั่น 12cแต่การค้นหาเอกสารของ Oracle และรุ่นที่ระบุนั้นค่อนข้างยาก
ypercubeᵀᴹ

2

คุณสามารถเข้าร่วมค่า Null โดยใช้การถอดรหัส:

on decode(t1.id, t2.id, 1, 0) = 1

decodeถือว่าเป็นโมฆะเท่ากันดังนั้นจึงใช้งานได้โดยไม่ต้องใช้หมายเลข "วิเศษ" สองคอลัมน์ต้องมีชนิดข้อมูลเดียวกัน

มันจะไม่สร้างโค้ดที่อ่านได้มากที่สุด แต่อาจจะยังดีกว่า t1.id = t2.id or (t1.id is null and t2.id is null)


1

ทำไมคุณไม่สามารถใช้ค่า Null ในการเข้าร่วมได้ ใน Oracle ทั้งสองอย่างต่อไปนี้ไม่ได้ประเมินว่าเป็นจริง:

  • NULL = NULL
  • NULL <> NULL

นั่นเป็นเหตุผลที่เราต้องIS NULL/ IS NOT NULLเพื่อตรวจสอบค่าว่าง
เพื่อทดสอบสิ่งนี้คุณสามารถทำได้:

SELECT * FROM table_name WHERE NULL = NULL

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

ฉันเดาว่าโมฆะไม่สามารถเท่ากับโมฆะในการเข้าร่วมเพื่อประโยชน์ของความสอดคล้อง มันจะท้าทายพฤติกรรมปกติของผู้ประกอบการเปรียบเทียบ


NULL = anythingผลลัพธ์เป็นNULLเพราะมาตรฐาน SQL บอกเช่นนั้น แถวเป็นไปตามเงื่อนไขการเข้าร่วมเฉพาะในกรณีที่นิพจน์เป็นจริง
Laurenz Albe

1
นอกเหนือจากรายละเอียดการใช้งานตามตัวอักษร (ซึ่งไม่ได้เป็นทุกกรณี: บางฐานข้อมูลมีตัวเลือกที่จะถือเอา NULL เป็น NULL เพื่อวัตถุประสงค์บางส่วน / ทั้งหมด) มีเหตุผลเชิงตรรกะ: NULL ไม่เป็นที่รู้จัก เมื่อคุณเปรียบเทียบ NULL กับ NULL คุณจะถามว่า "คือสิ่งที่ไม่รู้จักนี้เท่ากับสิ่งที่ไม่ทราบอื่น ๆ " ซึ่งคำตอบที่สมเหตุสมผลเพียงคำเดียวคือ "ไม่ทราบ" - NULL อื่น (ซึ่งถูกแมปเป็นเท็จในสถานการณ์เปรียบเทียบ)
David Spillett

-4

ค่า Null ในฐานข้อมูลเชิงสัมพันธ์ส่วนใหญ่ถือว่า UNKNOWN ไม่ควรสับสนกับศูนย์ HEX ทั้งหมด หากบางสิ่งมีโมฆะ (ไม่ทราบ) คุณไม่สามารถเปรียบเทียบได้

Unknown = Known False
Unknown = Unknown False
Unknown >= Known False
Known >= Unknown False

ซึ่งหมายความว่าเมื่อใดก็ตามที่คุณมีโมฆะในฐานะตัวถูกดำเนินการในนิพจน์บูลีนส่วนอื่นจะเป็นจริงเสมอ

ตรงกันข้ามกับความเกลียดชังทั่วไปที่มีต่อ null โดยนักพัฒนา null มีสถานที่ หากสิ่งที่ไม่รู้จักใช้ null


6
อันที่จริงทุกตัวอย่างการเปรียบเทียบที่คุณมีให้ผลUNKNOWNไม่ใช่FALSE;)
ypercubeᵀᴹ

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