(x is not NULL) vs (ไม่ใช่ x is NULL) ใน PostgreSQL


16

ทำไมx IS NOT NULLไม่เท่ากับNOT x IS NULL?

รหัสนี้:

CREATE TABLE bug_test (
    id int,
    name text
);

INSERT INTO bug_test
VALUES (1, NULL);

DO $$
DECLARE
    v_bug_test bug_test;
BEGIN
    RAISE NOTICE '%: %', v_bug_test, (v_bug_test IS NULL);
    RAISE NOTICE '%: %', v_bug_test, (v_bug_test IS NOT NULL);
    RAISE NOTICE '%: %', v_bug_test, (NOT v_bug_test IS NULL);

    SELECT *
    INTO v_bug_test
    FROM bug_test
    WHERE id = 1;

    RAISE NOTICE '%: %', v_bug_test, (v_bug_test IS NULL);
    RAISE NOTICE '%: %', v_bug_test, (v_bug_test IS NOT NULL);
    RAISE NOTICE '%: %', v_bug_test, (NOT v_bug_test IS NULL);
END
$$;

DROP TABLE bug_test;

ให้ผลลัพธ์ต่อไปนี้:

(,): t
(,): f
(,): f
(1,): f
(1,): f ???
(1,): t

ในขณะที่ฉันคาดว่าจะได้รับผลลัพธ์นี้:

(,): t
(,): f
(,): f
(1,): f
(1,): t <<<
(1,): t

1
คุณกำลังพิจารณาความจริงที่ว่าคุณกำลังตรวจสอบบันทึกทั้งหมดกับ NULL (คุณคือ
joanolo

@joanolo ใช่ ฉันได้เปลี่ยนรหัสเพื่อตรวจสอบidใน codebase จริงของฉัน แต่หลังจากใช้เวลาสองสามชั่วโมงเพื่อค้นหาปัญหา
Anil

1
ดูเหมือนว่าฉันrec_variable IS NOT NULLกำลังตรวจสอบว่าคอลัมน์ทั้งหมดไม่เป็นโมฆะในขณะที่rec_variable IS NULLกำลังตรวจสอบว่าคอลัมน์ทั้งหมดเป็นโมฆะ ดังนั้นNOT rec_variable IS NULLให้สิ่งที่ฉันคาดไว้ - คำตอบสำหรับคำถามที่ว่า
Anil

คำตอบ:


17

คุณต้องแยกแยะสถานการณ์สองสถานการณ์: คุณเปรียบเทียบหนึ่งคอลัมน์กับ NULL หรือคุณเปรียบเทียบ ROW ทั้งหมด (RECORD) กับ NULL

พิจารณาคำถามต่อไปนี้:

SELECT
    id, 
    txt, 
    txt     IS NULL AS txt_is_null, 
    NOT txt IS NULL AS not_txt_is_null, 
    txt IS NOT NULL AS txt_is_not_null
FROM
    (VALUES
        (1::integer, NULL::text)
    ) 
    AS x(id, txt) ;

คุณได้รับสิ่งนี้:

+----+-----+-------------+-----------------+-----------------+
| id | txt | txt_is_null | not_txt_is_null | txt_is_not_null | 
+----+-----+-------------+-----------------+-----------------+
|  1 |     | t           | f               | f               | 
+----+-----+-------------+-----------------+-----------------+

นี่คือสิ่งที่ฉันและคุณคาดหวัง คุณกำลังตรวจสอบคอลัมน์หนึ่งคอลัมน์จาก NULL และคุณได้รับ "txt IS NOT NULL" และ "NOT txt IS NULL" เทียบเท่ากัน

อย่างไรก็ตามหากคุณทำการตรวจสอบอื่น:

SELECT
    id, 
    txt, 
    x       IS NULL AS x_is_null,
    NOT x   IS NULL AS not_x_is_null,
    x   IS NOT NULL AS x_is_not_null
FROM
    (VALUES
        (1, NULL)
    ) 
    AS x(id, txt) ;

จากนั้นคุณจะได้รับ

+----+-----+-----------+---------------+---------------+
| id | txt | x_is_null | not_x_is_null | x_is_not_null |
+----+-----+-----------+---------------+---------------+
|  1 |     | f         | t             | f             |
+----+-----+-----------+---------------+---------------+

นี่อาจจะแปลกใจ สิ่งหนึ่งที่ดูสมเหตุสมผล (x IS NULL) และ (ไม่ใช่ x IS NULL) อยู่ตรงข้ามกัน อีกสิ่งหนึ่ง (ความจริงที่ว่า "x IS NULL" หรือ "x IS NULL NULL" ไม่เป็นความจริง) ดูแปลก ๆ

อย่างไรก็ตามนี่คือสิ่งที่เอกสาร PostgreSQLบอกว่าน่าจะเกิดขึ้น:

ถ้านิพจน์เป็นค่าแถวดังนั้น IS NULL จะเป็นจริงเมื่อนิพจน์แถวเป็นโมฆะหรือเมื่อเขตข้อมูลของแถวทั้งหมดเป็นโมฆะในขณะที่ IS NOT NULL เป็นจริงเมื่อนิพจน์แถวนั้นเป็นค่าว่างและฟิลด์ของแถวทั้งหมดเป็น ไม่ใช่ null เนื่องจากลักษณะการทำงานนี้เป็นค่า NULL และไม่ใช่ค่า NULL จึงไม่ส่งคืนผลลัพธ์แบบผกผันสำหรับนิพจน์ที่มีค่าเป็นแถวเสมอ โดยเฉพาะอย่างยิ่งนิพจน์ที่มีค่าของแถวที่มีทั้งฟิลด์ที่เป็นโมฆะและไม่เป็นโมฆะจะส่งคืนค่าเท็จสำหรับการทดสอบทั้งสอง ในบางกรณีมันอาจจะดีกว่าที่จะเขียนแถว DISTINCT จาก NULL หรือแถวไม่ใช่ DISTINCT FULL จาก NULL ซึ่งจะตรวจสอบว่าค่าแถวโดยรวมเป็นโมฆะโดยไม่ต้องทดสอบเพิ่มเติมใด ๆ ในฟิลด์แถว

ฉันต้องยอมรับว่าฉันไม่คิดว่าฉันเคยใช้การเปรียบเทียบแถวที่มีค่าเทียบกับค่าว่าง แต่ฉันเดาว่าถ้ามีความเป็นไปได้ที่นั่นอาจมีกรณีการใช้งานบางอย่าง ฉันไม่คิดว่าเป็นเรื่องปกติ แต่อย่างใด


ใช่คำอธิบายสมเหตุสมผลและตรงกับผลการทดลองที่ฉันทำตั้งแต่โพสต์สิ่งนี้ เหตุใดฉันจึงเปรียบเทียบตัวแปรเร็กคอร์ดทั้งหมดเนื่องจากพื้นหลังของฉันอยู่ในภาษาที่ไม่ใช่ SQL ซึ่งเป็นเรื่องปกติ เกี่ยวกับกรณีการใช้งานสิ่งนี้มีประโยชน์เมื่อเราต้องการตรวจสอบว่าทุกฟิลด์ในตัวแปรเรคคอร์ดนั้นเต็มไปด้วย (REC IS NOT NULL) แทนที่จะทำฟิลด์ตามฟิลด์
Anil

1
@Anil: กรณีการใช้งานที่คุณพูดถึงมีมาก่อน: stackoverflow.com/questions/21021102/…
Erwin Brandstetter
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.