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

16

Pourquoi n'est x IS NOT NULLpas égal à NOT x IS NULL?

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

donne la sortie suivante:

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

alors que je m'attendrais à obtenir cette sortie:

(,): t
(,): f
(,): f
(1,): f
(1,): t <<<
(1,): t
Anil
la source
1
Envisagez-vous le fait que vous vérifiez réellement un enregistrement entier par rapport à NULL. (Vous êtes
joanolo
@joanolo Oui. J'ai changé le code pour vérifier iddans ma vraie base de code, mais seulement après avoir passé quelques heures à chercher un problème.
Anil
1
Il me semble que rec_variable IS NOT NULLvérifie si toutes les colonnes ne sont PAS NULES, tandis que rec_variable IS NULLvérifie si toutes les colonnes sont NULES. Par conséquent NOT rec_variable IS NULLdonne ce que je m'y attendais - une réponse à la question « est - il quelque chose à l' intérieur? ».
Anil

Réponses:

17

Vous devez distinguer deux situations: vous comparez une COLONNE à NULL, ou vous comparez la RANG (ENREGISTREMENT) entière à NULL.

Considérez la requête suivante:

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

Vous obtenez ceci:

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

C'est, je suppose, ce à quoi vous et moi nous attendrions. Vous vérifiez une COLONNE par rapport à NULL et vous obtenez "txt IS NOT NULL" et "NOT txt IS NULL" sont équivalents.

Cependant, si vous effectuez une vérification différente:

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

Ensuite, vous obtenez

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

Cela peut surprendre. Une chose semble raisonnable (x EST NUL) et (PAS x EST NUL) sont l'opposé l'une de l'autre. L'autre chose (le fait que ni "x IS NULL" ni "x IS NOT NULL" ne soient vrais), semble bizarre.

Cependant, c'est ce que la documentation PostgreSQL indique qui devrait se produire:

Si l'expression a une valeur de ligne, IS NULL est vrai lorsque l'expression de ligne elle-même est nulle ou lorsque tous les champs de la ligne sont nuls, tandis que IS NOT NULL est vrai lorsque l'expression de ligne elle-même est non nulle et que tous les champs de la ligne sont non nul. En raison de ce comportement, IS NULL et IS NOT NULL ne renvoient pas toujours des résultats inverses pour les expressions à valeur de ligne; en particulier, une expression de valeur de ligne qui contient à la fois des champs null et non null renverra false pour les deux tests. Dans certains cas, il peut être préférable d'écrire la ligne IS DISTINCT FROM NULL ou la ligne IS NOT DISTINCT FROM NULL, qui vérifiera simplement si la valeur globale de la ligne est nulle sans aucun test supplémentaire sur les champs de ligne.

Je dois avouer que je ne pense pas avoir jamais utilisé de comparaison de valeur par ligne contre null, mais je suppose que si la possibilité est là, il pourrait y avoir un cas d'utilisation. Je ne pense pas que ce soit courant de toute façon.

joanolo
la source
Oui, l'explication a du sens et correspond aux résultats des expériences que j'ai faites depuis la publication de cela. Pourquoi j'ai comparé toute la variable d'enregistrement parce que mon arrière-plan est dans des langages non SQL, où cela est assez courant. En ce qui concerne les cas d'utilisation, je pense que c'est pratique lorsque l'on veut vérifier si tous les champs d'une variable d'enregistrement sont remplis (rec IS NOT NULL), au lieu de le faire champ par champ.
Anil
1
@Anil: Exactement le cas d'utilisation que vous mentionnez est apparu auparavant: stackoverflow.com/questions/21021102/…
Erwin Brandstetter