(x IST NICHT NULL) vs (NOT x IST NULL) in PostgreSQL

16

Warum ist das x IS NOT NULLnicht gleich NOT x IS NULL?

Dieser Code:

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;

gibt folgende Ausgabe aus:

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

während ich erwarten würde, diese Ausgabe zu erhalten:

(,): t
(,): f
(,): f
(1,): f
(1,): t <<<
(1,): t
Anil
quelle
1
Denken Sie darüber nach, dass Sie tatsächlich einen ganzen Datensatz mit NULL abgleichen. (Sie sind
Joanolo
@joanolo Ja. Ich habe den Code umgestellt, um nach idmeinem tatsächlichen Code zu suchen, aber erst, nachdem ich einige Stunden damit verbracht habe, nach einem Problem zu suchen.
Anil
1
Es scheint mir, dass rec_variable IS NOT NULLüberprüft wird, ob alle Spalten NICHT NULL sind, während rec_variable IS NULLüberprüft wird, ob alle Spalten NULL sind. Daher NOT rec_variable IS NULLgibt es das, was ich erwartet hatte - eine Antwort auf die Frage "Ist irgendetwas drin?".
Anil

Antworten:

17

Sie müssen zwei Situationen unterscheiden: Sie vergleichen eine SPALTE mit NULL oder Sie vergleichen die gesamte REIHE (RECORD) mit NULL.

Betrachten Sie die folgende Abfrage:

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

Du bekommst das:

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

Das ist, denke ich, was Sie und ich erwarten würden. Sie überprüfen eine Spalte gegen NULL, und Sie erhalten "txt IS NOT NULL" und "NOT txt IS NULL" sind äquivalent.

Wenn Sie jedoch eine andere Prüfung durchführen:

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

Dann bekommst du

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

Das mag überraschen. Eine Sache sieht vernünftig aus (x IST NULL) und (NICHT x IST NULL) sind das Gegenteil voneinander. Die andere Sache (die Tatsache, dass weder "x IS NULL" noch "x IS NOT NULL" wahr sind) sieht seltsam aus.

In der PostgreSQL-Dokumentation heißt es jedoch, dass Folgendes passieren sollte:

Wenn der Ausdruck einen Zeilenwert hat, ist IS NULL wahr, wenn der Zeilenausdruck selbst null ist, oder wenn alle Felder der Zeile null sind, während IS NOT NULL wahr ist, wenn der Zeilenausdruck selbst nicht null ist und alle Felder der Zeile null sind nicht null. Aufgrund dieses Verhaltens geben IS NULL und IS NOT NULL nicht immer inverse Ergebnisse für zeilenwertige Ausdrücke zurück. Insbesondere gibt ein zeilenwertiger Ausdruck, der sowohl Null- als auch Nicht-Null-Felder enthält, für beide Tests false zurück. In einigen Fällen kann es vorzuziehen sein, Zeile IS DISTINCT FROM NULL oder Zeile IS NOT DISTINCT FROM NULL zu schreiben, wodurch einfach geprüft wird, ob der Gesamtzeilenwert ohne zusätzliche Tests für die Zeilenfelder null ist.

Ich muss gestehen, ich glaube nicht, dass ich jemals einen Vergleich mit Zeilenwerten gegen null durchgeführt habe, aber ich denke, wenn die Möglichkeit besteht, könnte es einen Anwendungsfall dafür geben. Ich glaube sowieso nicht, dass es üblich ist.

joanolo
quelle
Ja, die Erklärung ist sinnvoll und stimmt mit den Ergebnissen der Experimente überein, die ich seit der Veröffentlichung durchgeführt habe. Ich habe die gesamte Datensatzvariable verglichen, weil mein Hintergrund in Nicht-SQL-Sprachen vorliegt, wo dies häufig vorkommt. In Bezug auf Anwendungsfälle ist dies praktisch, wenn überprüft werden soll, ob alle Felder in einer Datensatzvariablen gefüllt sind (rec IS NOT NULL), anstatt dies Feld für Feld zu tun.
Anil
1
@Anil: Genau der Anwendungsfall, den Sie erwähnt haben, ist bereits aufgetaucht: stackoverflow.com/questions/21021102/…
Erwin Brandstetter,