Oracle
 sql >> Datenbank >  >> RDS >> Oracle

NOT IN-Auswahl mit NULL-Werten

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.