Zunächst etwas Theorie:Null (SQL)
Die für uns wichtigsten Teile aus obigem Link:
Vergleiche mit NULL und der dreiwertigen Logik (3VL)
Da Null kein Mitglied einer Datendomäne ist, wird es nicht als "Wert" betrachtet, sondern als Markierung (oder Platzhalter), die das Fehlen eines Werts anzeigt. Aus diesem Grund können Vergleiche mit Null niemals True oder False ergeben, sondern immer ein drittes logisches Ergebnis, Unbekannt.[8] Das logische Ergebnis des nachstehenden Ausdrucks, der den Wert 10 mit Null vergleicht, ist Unbekannt:
SELECT 10 = NULL -- Results in Unknown
damit beide Vergleiche:x = NULL
und x <> NULL
ergibt NULL (unbekannt).
SQL implementiert drei logische Ergebnisse, daher müssen SQL-Implementierungen eine spezialisierte dreiwertige Logik (3VL) bereitstellen. Die Regeln für die dreiwertige SQL-Logik sind in den folgenden Tabellen dargestellt (p und q stellen logische Zustände dar)"[9] Die Wahrheitstabellen, die SQL für AND, OR und NOT verwendet, entsprechen einem gemeinsamen Fragment der dreiwertigen Logik von Kleene und Łukasiewicz ( die sich in ihrer Definition der Implikation unterscheiden, jedoch definiert SQL keine solche Operation).
+---------+-------------+-------------+-------------+-----------+--------+
| 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|
+---------+-------------+-------------+-------------+-----------+--------+
Auswirkung von Unbekannt in WHERE-Klauseln
Die dreiwertige SQL-Logik wird in der Data Manipulation Language (DML) in Vergleichsprädikaten von DML-Anweisungen und -Abfragen angetroffen. Die WHERE-Klausel bewirkt, dass die DML-Anweisung nur auf die Zeilen wirkt, für die das Prädikat Wahr ergibt.
Kurz gesagt:WHERE-Klausel behandelt NULL als FALSE
Betrachten Sie nun bitte einen einfacheren Fall:
SELECT * FROM T1;
| X |
|--------|
| 1 |
| (null) |
und eine Abfrage:
SELECT * FROM t1 WHERE x IN (1, NULL);
Die obige Abfrage ist eine Abkürzung zu dieser:
SELECT * FROM t1
WHERE x = 1
OR x = NULL
Für die zweite Zeile aus Tabelle t
( x =NULL) diese Bedingung sieht so aus:
WHERE NULL = 1
OR NULL = NULL
also diese Bedingung für die Zeile x=NULL
wird zu NULL ausgewertet, weil NULL=1
NULL ist, NULL=NULL
NULL ist, und NULL OR NULL
ist ebenfalls NULL (siehe Tabelle 3VL oben).
Betrachten Sie nun einen merkwürdigeren Fall:
SELECT * FROM t1 WHERE x NOT IN (1, NULL);
Diese Klausel x NOT IN (1, NULL)
entspricht NOT ( x IN (1, NULL) )
Also ist es auch äquivalent zu:
NOT (
x = 1
OR
x = NULL
)
und nach den Gesetzen von De Morgan ist es äquivalent zu:
NOT ( x = 1 ) AND NOT ( x = NULL )
und (wenn wir NOT x = y
ersetzen mit x <> y
) ist es auch äquivalent zu:
x <> 1 AND x <> NULL
Bitte schauen Sie sich die letzte Bedingung genau an:
WHERE
x <> 1 AND x <> NULL
Wir wissen als x <> NULL
wird immer zu NULL ausgewertet. Wir wissen auch aus der obigen 3VL-Tabelle, dass sowohl true AND NULL
ist NULL und false AND NULL
zu FALSE ausgewertet, also ergibt die gesamte Bedingung immer entweder FALSE oder NULL, aber nie TRUE.
Deshalb eine Abfrage mit dieser Bedingung:
SELECT .....
WHERE x NOT IN ( NULL, whatever)
gibt immer eine leere Ergebnismenge zurück
Und jetzt Ihre Frage, die auch neugierig ist:
SELECT * FROM t1
WHERE (id, val) NOT IN (select id, val from data2);
die umgeschrieben werden kann (mit konstanten Werten) zu:
SELECT * FROM t1
WHERE (id, val) NOT IN (
(1, null),
(2, 2 )
)
Diese Abfrage verwendet einen sogenannten Zeilenwertausdruck
Grundsätzlich eine Bedingung, die den Zeilenwertausdruck wie diesen verwendet
(a, b) = (x, y)
entspricht diesem:
a = x AND b = y
also kann die obige Abfrage in diese umgeschrieben werden:
SELECT * FROM t1
WHERE NOT (
id = 1 AND val = NULL
OR
id = 2 AND val = 2
)
Nach den Gesetzen von De Morgan ist dies identisch mit:
SELECT * FROM t1
WHERE
NOT ( id = 1 AND val = NULL )
AND
NOT ( id = 2 AND val = 2 )
und weiter zu:
SELECT * FROM t1
WHERE
( id <> 1 OR val <> NULL )
AND
( id <> 2 OR val <> 2 )
Seit dem ersten Teil ( id <> 1 OR val <> NULL )
der Bedingung wird nur dann als wahr ausgewertet, wenn id <> 1
(siehe 3VL-Tabelle oben), kann diese Bedingung vereinfacht werden zu:
SELECT * FROM t1
WHERE
( id <> 1 )
AND
( id <> 2 OR val <> 2 )
und weiter (nach den Gesetzen von De Morgan) in:
SELECT * FROM t1
WHERE
id <> 1 AND id <> 2
OR
id <> 1 AND val <> 2
also weder (1,1)
noch (2,2)
aus der Quelle data1
diese Bedingungen einhalten.