(x NO ES NULO) vs (NO x ES NULO) en PostgreSQL

16

¿Por qué x IS NOT NULLno es igual a NOT x IS NULL?

Este código:

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;

da el siguiente resultado:

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

mientras que esperaría obtener esta salida:

(,): t
(,): f
(,): f
(1,): f
(1,): t <<<
(1,): t
Añil
fuente
1
¿Está considerando el hecho de que en realidad está comprobando un registro completo contra NULL? (Estás
joanolo
@joanolo Sí. He cambiado el código para verificarlo iden mi base de código real, pero solo después de pasar algunas horas buscando un problema.
Anil
1
Me parece que rec_variable IS NOT NULLestá comprobando si todas las columnas NO SON NULAS, mientras rec_variable IS NULLestá comprobando si todas las columnas son NULAS. Por NOT rec_variable IS NULLlo tanto, da lo que esperaba: una respuesta a la pregunta "¿hay algo dentro?".
Anil

Respuestas:

17

Debe distinguir dos situaciones: compara una COLUMNA con NULL, o compara toda la FILA (REGISTRO) con NULL.

Considere la siguiente consulta:

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

Obtienes esto:

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

Esto es, supongo, lo que tú y yo esperaríamos. Está comprobando una COLUMNA contra NULL, y obtiene "txt IS NOT NULL" y "NOT txt IS NULL" son equivalentes.

Sin embargo, si realiza una comprobación diferente:

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

Entonces obtienes

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

Esto puede ser sorprendente. Una cosa parece razonable (x IS NULL) y (NOT x IS NULL) son lo opuesto el uno del otro. La otra cosa (el hecho de que ni "x IS NULL" ni "x IS NOT NULL" son verdaderos) parece extraño.

Sin embargo, esto es lo que dice la documentación de PostgreSQL que debería suceder:

Si la expresión tiene un valor de fila, IS NULL es verdadero cuando la expresión de fila en sí es nula o cuando todos los campos de la fila son nulos, mientras que IS NOT NULL es verdadero cuando la expresión de fila en sí no es nula y todos los campos de la fila son nulos. no nulo Debido a este comportamiento, IS NULL y IS NOT NULL no siempre devuelven resultados inversos para expresiones con valores de fila; en particular, una expresión con valores de fila que contiene campos nulos y no nulos devolverá falso para ambas pruebas. En algunos casos, puede ser preferible escribir fila IS DISTINCT FROM NULL o row IS NOT DISTINCT FROM NULL, que simplemente comprobará si el valor general de la fila es nulo sin ninguna prueba adicional en los campos de la fila.

Debo confesar que no creo que alguna vez haya usado una comparación de valores de fila contra nulo, pero supongo que si existe la posibilidad, podría haber algún caso de uso para ello. No creo que sea común, de todos modos.

joanolo
fuente
Sí, la explicación tiene sentido y coincide con los resultados de los experimentos que hice desde que publiqué esto. La razón por la que comparé toda la variable de registro es porque mi fondo está en lenguajes que no son SQL, donde esto es bastante común. Con respecto a los casos de uso, creo que esto es útil cuando uno quiere verificar si todos los campos en una variable de registro están llenos (rec NO ES NULO), en lugar de hacerlo campo por campo.
Anil
1
@Anil: exactamente el caso de uso que mencionas ha aparecido antes: stackoverflow.com/questions/21021102/…
Erwin Brandstetter