Prima un po' di teoria:Null (SQL)
Le parti più importanti per noi dal link sopra:
Confronto con NULL e la logica a tre valori (3VL)
Poiché Null non è un membro di alcun dominio di dati, non è considerato un "valore", ma piuttosto un marker (o segnaposto) che indica l'assenza di valore. Per questo motivo, i confronti con Null non possono mai risultare né Vero né Falso, ma sempre in un terzo risultato logico, Sconosciuto.[8] Il risultato logico dell'espressione seguente, che confronta il valore 10 con Null, è Sconosciuto:
SELECT 10 = NULL -- Results in Unknown
in modo che entrambi i confronti:x = NULL
e x <> NULL
restituisce NULL(sconosciuto).
SQL implementa tre risultati logici, quindi le implementazioni SQL devono fornire una logica a tre valori specializzata (3VL). Le regole che governano la logica a tre valori di SQL sono mostrate nelle tabelle seguenti (p eq rappresentano gli stati logici)"[9] Le tabelle di verità utilizzate da SQL per AND, OR e NOT corrispondono a un frammento comune della logica a tre valori di Kleene e Łukasiewicz ( che differiscono nella loro definizione di implicazione, tuttavia SQL non definisce tale operazione).
+---------+-------------+-------------+-------------+-----------+--------+
| p | q | p OR q | p AND q | p = q |p != q |
+---------+-------------+-------------+-------------+-----------+--------+
| True | True | True | True | True | False |
| True | False | True | False | False | True |
| True | Unknown | True | Unknown | Unknown | Unknown|
| False | True | True | False | False | True |
| False | False | False | False | True | False |
| False | Unknown | Unknown | False | Unknown | Unknown|
| Unknown | True | True | Unknown | Unknown | Unknown|
| Unknown | False | Unknown | False | Unknown | Unknown|
| Unknown | Unknown | Unknown | Unknown | Unknown | Unknown|
+---------+-------------+-------------+-------------+-----------+--------+
Effetto di Sconosciuto nelle clausole WHERE
La logica SQL a tre valori viene rilevata in Data Manipulation Language (DML) nei predicati di confronto di istruzioni e query DML. La clausola WHERE fa sì che l'istruzione DML agisca solo su quelle righe per le quali il predicato restituisce True.
Quindi, in breve:la clausola WHERE considera NULL come FALSO
Consideriamo ora un caso più semplice:
SELECT * FROM T1;
| X |
|--------|
| 1 |
| (null) |
e una domanda:
SELECT * FROM t1 WHERE x IN (1, NULL);
La query di cui sopra è una scorciatoia per questa:
SELECT * FROM t1
WHERE x = 1
OR x = NULL
Per la seconda riga della tabella t
( x =NULL) questa condizione è simile a:
WHERE NULL = 1
OR NULL = NULL
quindi questa condizione per la riga x=NULL
restituisce NULL perché NULL=1
è NULL, NULL=NULL
è NULL e NULL OR NULL
è anche NULL (consultare la tabella 3VL sopra).
Consideriamo ora un caso più curioso:
SELECT * FROM t1 WHERE x NOT IN (1, NULL);
Questa clausola x NOT IN (1, NULL)
è equivalente a NOT ( x IN (1, NULL) )
quindi equivale anche a:
NOT (
x = 1
OR
x = NULL
)
e secondo le leggi di De Morgan equivale a:
NOT ( x = 1 ) AND NOT ( x = NULL )
e (se sostituiamo NOT x = y
con x <> y
) è anche equivalente a:
x <> 1 AND x <> NULL
Guarda attentamente l'ultima condizione:
WHERE
x <> 1 AND x <> NULL
Sappiamo di x <> NULL
restituisce sempre NULL. Sappiamo anche dalla tabella 3VL sopra, che sia true AND NULL
è NULL e false AND NULL
restituisce FALSE, quindi l'intera condizione restituisce sempre FALSE o NULL, ma non restituisce mai TRUE.
Quindi una query con questa condizione:
SELECT .....
WHERE x NOT IN ( NULL, whatever)
Restituisce sempre un set di risultati vuoto
E ora la tua domanda, che è anche curiosa:
SELECT * FROM t1
WHERE (id, val) NOT IN (select id, val from data2);
che può essere riscritto (usando valori costanti) in:
SELECT * FROM t1
WHERE (id, val) NOT IN (
(1, null),
(2, 2 )
)
Questa query utilizza la cosiddetta espressione del valore di riga
Sostanzialmente una condizione che utilizza il valore di riga espresso in questo modo
(a, b) = (x, y)
è equivalente a questo:
a = x AND b = y
quindi la query sopra può essere riscritta in questa:
SELECT * FROM t1
WHERE NOT (
id = 1 AND val = NULL
OR
id = 2 AND val = 2
)
Secondo le leggi di De Morgan questo è identico a:
SELECT * FROM t1
WHERE
NOT ( id = 1 AND val = NULL )
AND
NOT ( id = 2 AND val = 2 )
e oltre a:
SELECT * FROM t1
WHERE
( id <> 1 OR val <> NULL )
AND
( id <> 2 OR val <> 2 )
Dalla prima parte ( id <> 1 OR val <> NULL )
della condizione restituisce true solo nel caso in cui id <> 1
(consultare la tabella 3VL sopra), questa condizione può essere semplificata in:
SELECT * FROM t1
WHERE
( id <> 1 )
AND
( id <> 2 OR val <> 2 )
e inoltre (secondo le leggi di De Morgan) in:
SELECT * FROM t1
WHERE
id <> 1 AND id <> 2
OR
id <> 1 AND val <> 2
quindi nemmeno (1,1)
né (2,2)
dalla fonte data1
rispettare queste condizioni.