Först lite teori:Null (SQL)
De viktigaste delarna för oss från länken ovan:
Jämförelser med NULL och logiken med tre värden (3VL)
Eftersom Null inte är medlem i någon datadomän anses det inte vara ett "värde", utan snarare en markör (eller platshållare) som indikerar frånvaron av värde. På grund av detta kan jämförelser med Null aldrig leda till varken Sant eller Falskt, utan alltid i ett tredje logiskt resultat, Okänt.[8] Det logiska resultatet av uttrycket nedan, som jämför värdet 10 med Null, är okänt:
SELECT 10 = NULL -- Results in Unknown
så att båda jämförelserna:x = NULL
och x <> NULL
utvärderas till NULL(okänd).
SQL implementerar tre logiska resultat, så SQL-implementationer måste tillhandahålla en specialiserad logik med tre värden (3VL). Reglerna för SQL-logik med tre värden visas i tabellerna nedan (p ochq representerar logiska tillstånd)"[9] Sanningstabellerna SQL använder för OCH, ELLER och INTE motsvarar ett vanligt fragment av Kleene- och Łukasiewicz-logiken med tre värden ( som skiljer sig i sin definition av implikation, men SQL definierar ingen sådan 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| +---------+-------------+-------------+-------------+-----------+--------+
Effekt av okänt i WHERE-satser
SQL-logik med tre värden påträffas i Data Manipulation Language (DML) i jämförelse av predikat av DML-satser och frågor. WHERE-satsen gör att DML-satsen endast agerar på de rader för vilka predikatet utvärderas till True.
Så kort sagt:WHERE-satsen behandlar NULL som FALSE
Tänk nu på ett enklare fall:
SELECT * FROM T1;
| X |
|--------|
| 1 |
| (null) |
och en fråga:
SELECT * FROM t1 WHERE x IN (1, NULL);
Ovanstående fråga är ett kortland till denna:
SELECT * FROM t1
WHERE x = 1
OR x = NULL
För den andra raden från tabellen t
( x =NULL) det här villkoret ser ut så här:
WHERE NULL = 1
OR NULL = NULL
så detta villkor för raden x=NULL
utvärderas till NULL eftersom NULL=1
är NULL, NULL=NULL
är NULL och NULL OR NULL
är också NULL (se tabell 3VL ovan).
Tänk nu på mer märkliga fall:
SELECT * FROM t1 WHERE x NOT IN (1, NULL);
Denna sats x NOT IN (1, NULL)
motsvarar NOT ( x IN (1, NULL) )
så det motsvarar också:
NOT (
x = 1
OR
x = NULL
)
och enligt De Morgans lagar motsvarar det:
NOT ( x = 1 ) AND NOT ( x = NULL )
och (om vi ersätter NOT x = y
med x <> y
) det motsvarar också:
x <> 1 AND x <> NULL
Titta noga på det sista villkoret:
WHERE
x <> 1 AND x <> NULL
Vi vet än x <> NULL
utvärderas alltid till NULL. Vi vet också från 3VL-tabellen ovan att både true AND NULL
är NULL och false AND NULL
evalueras till FALSE, så hela villkoret utvärderas alltid till FALSE eller NULL, men det evalueras aldrig till TRUE.
Därför en fråga med detta villkor:
SELECT .....
WHERE x NOT IN ( NULL, whatever)
returerar alltid tomma resultat
Och nu din fråga, som också är nyfiken:
SELECT * FROM t1
WHERE (id, val) NOT IN (select id, val from data2);
som kan skrivas om (med konstanta värden) till:
SELECT * FROM t1
WHERE (id, val) NOT IN (
(1, null),
(2, 2 )
)
Den här frågan använder så kallat radvärdeuttryck
I grund och botten ett villkor som använder radvärdet uttryck i så här
(a, b) = (x, y)
motsvarar denna:
a = x AND b = y
så ovanstående fråga kan skrivas om till denna:
SELECT * FROM t1
WHERE NOT (
id = 1 AND val = NULL
OR
id = 2 AND val = 2
)
Enligt De Morgans lagar är detta identiskt med:
SELECT * FROM t1
WHERE
NOT ( id = 1 AND val = NULL )
AND
NOT ( id = 2 AND val = 2 )
och vidare till:
SELECT * FROM t1
WHERE
( id <> 1 OR val <> NULL )
AND
( id <> 2 OR val <> 2 )
Sedan den första delen ( id <> 1 OR val <> NULL )
av villkoret utvärderas till sant endast i ett fall där id <> 1
(se 3VL-tabellen ovan), detta villkor kan förenklas till:
SELECT * FROM t1
WHERE
( id <> 1 )
AND
( id <> 2 OR val <> 2 )
och vidare (enligt De Morgans lagar) till:
SELECT * FROM t1
WHERE
id <> 1 AND id <> 2
OR
id <> 1 AND val <> 2
så varken (1,1)
inte heller (2,2)
från källkoden data1
följa dessa villkor.