Първо малко теория:Null (SQL)
Най-важните части за нас от горната връзка:
Сравнения с NULL и тризначната логика (3VL)
Тъй като Null не е член на нито един домейн на данни, той не се счита за "стойност", а по-скоро за маркер (или заместител), указващ отсъствието на стойност. Поради това сравненията с Null никога не могат да доведат нито до вярно, нито до грешно, а винаги до трети логически резултат, неизвестен.[8] Логическият резултат от израза по-долу, който сравнява стойността 10 с Null, е Неизвестен:
SELECT 10 = NULL -- Results in Unknown
така че и двете сравнения:x = NULL
и x <> NULL
оценява на NULL (неизвестно).
SQL реализира три логически резултата, така че SQL реализациите трябва да осигуряват специализирана тризначна логика (3VL). Правилата, управляващи тризначната логика на SQL, са показани в таблиците по-долу (p иq представляват логически състояния)"[9] Таблиците на истинността, които SQL използва за И, ИЛИ и НЕ съответстват на общ фрагмент от тризначната логика на Kleene и Łukasiewicz ( които се различават по своята дефиниция на импликация, но SQL не дефинира такава операция).
+---------+-------------+-------------+-------------+-----------+--------+ | 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| +---------+-------------+-------------+-------------+-----------+--------+
Ефект на неизвестно в клаузите WHERE
SQL тризначна логика се среща в езика за манипулиране на данни (DML) в предикатите за сравнение на DML изрази и заявки. Клаузата WHERE кара DML оператора да действа само върху онези редове, за които предикатът оценява на True.
Така накратко:клаузата WHERE третира NULL като FALSE
Сега, моля, помислете за по-прост случай:
SELECT * FROM T1;
| X |
|--------|
| 1 |
| (null) |
и заявка:
SELECT * FROM t1 WHERE x IN (1, NULL);
Горната заявка е кратка за тази:
SELECT * FROM t1
WHERE x = 1
OR x = NULL
За втория ред от таблица t
( x =NULL) това условие изглежда така:
WHERE NULL = 1
OR NULL = NULL
така че това условие за реда x=NULL
оценява на NULL, защото NULL=1
е NULL, NULL=NULL
е NULL и NULL OR NULL
също е NULL (моля, вижте таблицата 3VL по-горе).
Сега помислете за по-любопитен случай:
SELECT * FROM t1 WHERE x NOT IN (1, NULL);
Тази клауза x NOT IN (1, NULL)
е еквивалентен на NOT ( x IN (1, NULL) )
така че също е еквивалентен на:
NOT (
x = 1
OR
x = NULL
)
и според законите на Де Морган е еквивалентен на:
NOT ( x = 1 ) AND NOT ( x = NULL )
и (ако заменим NOT x = y
с x <> y
) също е еквивалентен на:
x <> 1 AND x <> NULL
Моля, разгледайте внимателно последното условие:
WHERE
x <> 1 AND x <> NULL
Знаем от x <> NULL
винаги се оценява на NULL. Също така знаем от таблицата 3VL по-горе, че и двете true AND NULL
е NULL и false AND NULL
оценява на FALSE, така че цялото условие винаги се оценява на FALSE или NULL, но никога не се оценява на TRUE.
Следователно заявка с това условие:
SELECT .....
WHERE x NOT IN ( NULL, whatever)
винаги връща празен набор от резултати
И сега вашата заявка, която също е любопитна:
SELECT * FROM t1
WHERE (id, val) NOT IN (select id, val from data2);
който може да бъде пренаписан (използвайки константни стойности) на:
SELECT * FROM t1
WHERE (id, val) NOT IN (
(1, null),
(2, 2 )
)
Тази заявка използва така наречения израз за стойност на ред
По принцип условие, използващо изразената стойност на реда като този
(a, b) = (x, y)
е еквивалентен на този:
a = x AND b = y
така че горната заявка може да бъде пренаписана в тази:
SELECT * FROM t1
WHERE NOT (
id = 1 AND val = NULL
OR
id = 2 AND val = 2
)
Според законите на Де Морган това е идентично на:
SELECT * FROM t1
WHERE
NOT ( id = 1 AND val = NULL )
AND
NOT ( id = 2 AND val = 2 )
и по-нататък:
SELECT * FROM t1
WHERE
( id <> 1 OR val <> NULL )
AND
( id <> 2 OR val <> 2 )
От първата част ( id <> 1 OR val <> NULL )
на условието се оценява на вярно само в случай, когато id <> 1
(моля, вижте таблицата 3VL по-горе), това условие може да бъде опростено до:
SELECT * FROM t1
WHERE
( id <> 1 )
AND
( id <> 2 OR val <> 2 )
и по-нататък (според законите на Де Морган) в:
SELECT * FROM t1
WHERE
id <> 1 AND id <> 2
OR
id <> 1 AND val <> 2
така че нито (1,1)
нито (2,2)
от изходния data1
спазват тези условия.