(x НЕ NULL) vs (NOT 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в реальній кодовій базі даних, але лише витративши кілька годин на пошуки проблеми.
Аніль

1
Мені здається, rec_variable IS NOT NULLце перевірка, чи всі стовпці НЕ NULL, а rec_variable IS NULLперевіряє, чи всі стовпці NULL. Звідси NOT rec_variable IS NULLдає те, що я очікував - відповідь на питання "чи все є всередині?".
Аніль

Відповіді:


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               | 
+----+-----+-------------+-----------------+-----------------+

Думаю, це те, чого ми з вами очікували. Ви перевіряєте одну COLUMN проти NULL, і ви отримуєте "txt 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) і (NOT x IS NULL) є протилежними один одному. Інша річ (той факт, що ні "x IS NULL", ні "x IS NULL" не відповідають дійсності) виглядає дивно.

Однак ось що говорить у документації PostgreSQL, це має відбутися:

Якщо вираз є рядковим, тоді IS NULL є істинним, коли сам вираз рядка є нульовим або коли всі поля рядка є нульовими, тоді як IS NOT NULL є істинним, коли вираження рядка не є нульовим, і всі поля рядка ненульовий. Через таку поведінку IS NULL і IS NOT NULL не завжди повертають обернені результати для виразів, що оцінюються за рядками; зокрема, рядкове значення, яке містить і нульові, і ненульові поля, поверне помилковим для обох тестів. У деяких випадках може бути кращим записати рядок IS DISTINCT OF NULL або рядок НЕ DISTINCT OF NULL, який просто перевірить, чи загальне значення рядка є нульовим без додаткових тестів у полях рядків.

Я мушу зізнатися, я не думаю, що я ніколи не використовував порівняльне значення у рядках проти нуля, але я здогадуюсь, що якщо така можливість є, для цього може бути якийсь корисний випадок. Я не думаю, що це звичайно.


Так, пояснення має сенс і відповідає результатам експериментів, які я робив з моменту публікації. Чому я порівнював всю змінну записів, тому що мій фон знаходиться в мовах, що не належать до SQL, де це досить часто. Що стосується випадків використання, я знаходжу це зручно, коли хочеться перевірити, чи заповнені всі поля в змінній запису (rec НЕ NULL), а не робити це поле за полем.
Аніль

1
@Anil: Точно той випадок використання, який ви згадуєте, вискочив раніше: stackoverflow.com/questions/21021102/…
Ервін Брандстеттер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.