(x IS NOT NULL) vs (NOT x IS NULL) di PostgreSQL

16

Kenapa x IS NOT NULLtidak sama dengan NOT x IS NULL?

Kode ini:

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;

memberikan hasil sebagai berikut:

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

sementara saya berharap untuk mendapatkan output ini:

(,): t
(,): f
(,): f
(1,): f
(1,): t <<<
(1,): t
Anil
sumber
1
Apakah Anda mempertimbangkan fakta bahwa Anda benar-benar memeriksa seluruh catatan terhadap NULL. (Anda
joanolo
@ Joanolo Ya. Saya telah mengganti kode untuk memeriksa idbasis kode saya yang sebenarnya, tetapi hanya setelah menghabiskan beberapa jam mencari masalah.
Anil
1
Sepertinya saya yang rec_variable IS NOT NULLmemeriksa apakah semua kolom TIDAK NULL, sementara rec_variable IS NULLmemeriksa apakah semua kolom NULL. Oleh karena itu NOT rec_variable IS NULLberikan apa yang saya harapkan - jawaban untuk pertanyaan "apakah ada sesuatu di dalamnya?".
Anil

Jawaban:

17

Anda harus membedakan dua situasi: Anda membandingkan satu KOLOM dengan NULL, atau Anda membandingkan seluruh ROW (REKAM) dengan NULL.

Pertimbangkan pertanyaan berikut:

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) ;

Anda mendapatkan ini:

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

Ini, saya kira, apa yang Anda dan saya harapkan. Anda memeriksa satu KOLOM terhadap NULL, dan Anda mendapatkan "txt IS NOT NULL" dan "NOT txt IS NULL" adalah setara.

Namun, jika Anda melakukan pemeriksaan yang berbeda:

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) ;

Lalu kamu dapatkan

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

Ini mungkin mengejutkan. Satu hal terlihat masuk akal (x IS NULL) dan (BUKAN x IS NULL) adalah kebalikan dari satu sama lain. Hal lain (fakta bahwa "x IS NULL" atau "x IS NOT NULL" tidak benar), terlihat aneh.

Namun, inilah yang menurut dokumentasi PostgreSQL harus terjadi:

Jika ekspresi bernilai baris, maka IS NULL benar ketika ekspresi baris itu sendiri adalah nol atau ketika semua bidang baris adalah nol, sedangkan IS NOT NULL benar ketika ekspresi baris itu sendiri adalah non-nol dan semua bidang baris adalah tidak nol. Karena perilaku ini, IS NULL dan IS NOT NULL tidak selalu mengembalikan hasil terbalik untuk ekspresi bernilai baris; khususnya, ekspresi bernilai baris yang berisi bidang nol dan non-nol akan menghasilkan false untuk kedua pengujian. Dalam beberapa kasus, mungkin lebih baik menulis baris IS DISTINCT FROM NULL atau row NOTT THTINCT FROM NULL, yang hanya akan memeriksa apakah keseluruhan nilai baris adalah nol tanpa tes tambahan pada bidang baris.

Saya harus mengakui saya tidak berpikir saya pernah menggunakan perbandingan nilai-baris terhadap nol, tapi saya kira jika ada kemungkinan, mungkin ada beberapa use-case untuk itu. Saya pikir itu tidak umum.

joanolo
sumber
Ya, penjelasannya masuk akal dan cocok dengan hasil percobaan yang saya lakukan sejak memposting ini. Mengapa saya membandingkan seluruh variabel catatan adalah karena latar belakang saya dalam bahasa non-SQL, di mana ini sangat umum. Mengenai kasus penggunaan, saya kira ini berguna ketika seseorang ingin memeriksa apakah semua bidang dalam variabel rekaman diisi (rec TIDAK BUKAN), alih-alih melakukannya bidang demi bidang.
Anil
1
@Anil: Persisnya use case yang Anda sebutkan telah muncul sebelumnya: stackoverflow.com/questions/21021102/…
Erwin Brandstetter