Najpierw trochę teorii:Null (SQL)
Najważniejsze dla nas części z powyższego linku:
Porównania z NULL i logiką trójwartościową (3VL)
Ponieważ Null nie należy do żadnej domeny danych, nie jest uważany za „wartość”, ale raczej za znacznik (lub symbol zastępczy) wskazujący na brak wartości. Z tego powodu porównania z wartością Null nigdy nie mogą skutkować ani prawdą, ani fałszem, ale zawsze trzecim logicznym wynikiem, nieznanym.[8] Logiczny wynik poniższego wyrażenia, które porównuje wartość 10 do Null, jest nieznany:
SELECT 10 = NULL -- Results in Unknown
aby oba porównania:x = NULL
i x <> NULL
ocenia na NULL(nieznane).
SQL implementuje trzy logiczne wyniki, więc implementacje SQL muszą zapewniać wyspecjalizowaną logikę trójwartościową (3VL). Reguły rządzące trójwartościową logiką SQL są pokazane w poniższych tabelach (p i q reprezentują stany logiczne)”[9] Tabele prawdy używane przez SQL dla AND, OR i NOT odpowiadają wspólnemu fragmentowi trójwartościowej logiki Kleene i Łukasiewicza ( które różnią się definicją implikacji, jednak SQL nie definiuje takiej operacji).
+---------+-------------+-------------+-------------+-----------+--------+
| 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|
+---------+-------------+-------------+-------------+-----------+--------+
Efekt nieznany w klauzulach WHERE
Trójwartościowa logika SQL występuje w języku manipulacji danymi (DML) w predykatach porównawczych instrukcji i zapytań DML. Klauzula WHERE powoduje, że instrukcja DML działa tylko na tych wierszach, dla których predykat ma wartość Prawda.
W skrócie:klauzula WHERE traktuje NULL jako FALSE
Rozważ teraz prostszy przypadek:
SELECT * FROM T1;
| X |
|--------|
| 1 |
| (null) |
i zapytanie:
SELECT * FROM t1 WHERE x IN (1, NULL);
Powyższe zapytanie jest skrótem do tego:
SELECT * FROM t1
WHERE x = 1
OR x = NULL
Dla drugiego wiersza z tabeli t
( x =NULL) ten warunek wygląda następująco:
WHERE NULL = 1
OR NULL = NULL
więc ten warunek dla wiersza x=NULL
ocenia na NULL, ponieważ NULL=1
ma wartość NULL, NULL=NULL
ma wartość NULL, a NULL OR NULL
jest również NULL (patrz tabela 3VL powyżej).
Rozważmy teraz ciekawszy przypadek:
SELECT * FROM t1 WHERE x NOT IN (1, NULL);
Ta klauzula x NOT IN (1, NULL)
jest równoważne NOT ( x IN (1, NULL) )
więc jest to również równoważne:
NOT (
x = 1
OR
x = NULL
)
i zgodnie z prawami De Morgana jest to równoważne:
NOT ( x = 1 ) AND NOT ( x = NULL )
i (jeśli zastąpimy NOT x = y
z x <> y
) jest również odpowiednikiem:
x <> 1 AND x <> NULL
Przyjrzyj się uważnie ostatniemu warunkowi:
WHERE
x <> 1 AND x <> NULL
Wiemy, że x <> NULL
zawsze ma wartość NULL. Wiemy również z powyższej tabeli 3VL, że zarówno true AND NULL
jest NULL i false AND NULL
zwraca FALSE, więc cały warunek zawsze zwraca FALSE lub NULL, ale nigdy nie zwraca TRUE.
Dlatego zapytanie z tym warunkiem:
SELECT .....
WHERE x NOT IN ( NULL, whatever)
zawsze zwraca pusty zestaw wyników
A teraz twoje zapytanie, które również jest ciekawe:
SELECT * FROM t1
WHERE (id, val) NOT IN (select id, val from data2);
które można przepisać (przy użyciu stałych wartości) na:
SELECT * FROM t1
WHERE (id, val) NOT IN (
(1, null),
(2, 2 )
)
To zapytanie używa tak zwanego wyrażenia wartości wiersza
Zasadniczo warunek używający wyrażenia wartości wiersza w ten sposób
(a, b) = (x, y)
jest odpowiednikiem tego:
a = x AND b = y
więc powyższe zapytanie można przepisać na to:
SELECT * FROM t1
WHERE NOT (
id = 1 AND val = NULL
OR
id = 2 AND val = 2
)
Zgodnie z prawami De Morgana jest to identyczne z:
SELECT * FROM t1
WHERE
NOT ( id = 1 AND val = NULL )
AND
NOT ( id = 2 AND val = 2 )
i dalej do:
SELECT * FROM t1
WHERE
( id <> 1 OR val <> NULL )
AND
( id <> 2 OR val <> 2 )
Od pierwszej części ( id <> 1 OR val <> NULL )
warunku ma wartość prawda tylko w przypadku, gdy id <> 1
(patrz tabela 3VL powyżej), ten warunek można uprościć do:
SELECT * FROM t1
WHERE
( id <> 1 )
AND
( id <> 2 OR val <> 2 )
i dalej (zgodnie z prawem De Morgana) na:
SELECT * FROM t1
WHERE
id <> 1 AND id <> 2
OR
id <> 1 AND val <> 2
więc ani (1,1)
ani (2,2)
ze źródła data1
przestrzegać tych warunków.