(x NON È NULL) vs (NON x È NULL) in PostgreSQL


16

Perché x IS NOT NULLnon è uguale a NOT x IS NULL?

Questo codice:

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;

dà il seguente output:

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

mentre mi aspetterei di ottenere questo risultato:

(,): t
(,): f
(,): f
(1,): f
(1,): t <<<
(1,): t

1
Stai considerando il fatto che stai effettivamente controllando un intero record contro NULL. (Sei
joanolo il

@joanolo Sì. Ho cambiato il codice per verificare idnella mia base di codice reale, ma solo dopo aver trascorso alcune ore a cercare un problema.
Anil,

1
Mi sembra che rec_variable IS NOT NULLstia verificando se tutte le colonne NON sono NULL, mentre rec_variable IS NULLsta controllando se tutte le colonne sono NULL. Quindi NOT rec_variable IS NULLdà quello che mi aspettavo: una risposta alla domanda "c'è qualcosa dentro?".
Anil,

Risposte:


17

Devi distinguere due situazioni: si confronta una COLONNA con NULL o si confronta l'intero ROW (RECORD) con NULL.

Considera la seguente query:

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

Ottieni questo:

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

Questo è, immagino, cosa ci aspetteremmo io e te. Stai controllando una COLONNA contro NULL e ottieni "txt IS NOT NULL" e "NOT txt IS NULL" sono equivalenti.

Tuttavia, se si esegue un controllo diverso:

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

Quindi ottieni

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

Questo può essere sorprendente. Una cosa sembra ragionevole (x IS NULL) e (NOT x IS NULL) sono l'opposto dell'altro. L'altra cosa (il fatto che né "x IS NULL" né "x IS NOT NULL" sono vere) sembra strano.

Tuttavia, questo è ciò che dice la documentazione di PostgreSQL che dovrebbe accadere:

Se l'espressione ha un valore di riga, allora IS NULL è vero quando l'espressione di riga stessa è nulla o quando tutti i campi della riga sono nulli, mentre IS NOT NULL è vera quando l'espressione di riga stessa non è nulla e tutti i campi della riga sono non nullo. A causa di questo comportamento, IS NULL e IS NOT NULL non restituiscono sempre risultati inversi per le espressioni con valori di riga; in particolare, un'espressione con valore di riga che contiene sia i campi null che non null restituirà false per entrambi i test. In alcuni casi, potrebbe essere preferibile scrivere la riga IS DISTINCT FROM NULL o la riga IS NOT DISTINCT FROM NULL, che controllerà semplicemente se il valore complessivo della riga è nullo senza ulteriori test sui campi della riga.

Devo confessare che non credo di aver mai usato un confronto con valori di riga rispetto a null, ma immagino che se esiste la possibilità, potrebbe esserci un caso d'uso per questo. Non penso sia comune, comunque.


Sì, la spiegazione ha senso e corrisponde ai risultati degli esperimenti che ho fatto da quando ho pubblicato questo. Il motivo per cui ho confrontato l'intera variabile record è perché il mio background è in linguaggi non SQL, dove questo è abbastanza comune. Per quanto riguarda i casi d'uso, mi risulta utile quando si vuole verificare se tutti i campi in una variabile record sono riempiti (rec IS NOT NULL), invece di farlo campo per campo.
Anil,

1
@Anil: il caso d'uso che hai menzionato è già apparso prima: stackoverflow.com/questions/21021102/…
Erwin Brandstetter,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.