привет
Есть две связанные таблицы: parent и child. Таблица parent:
parent_id
---------
| 1|
| 2|
| 3|
---------
child:
id parent_fk
--------------
| 1| NULL|
| 2| 1|
| 3| NULL|
| 4| 3|
--------------
При этом запрос:
SELECT * FROM parent
WHERE parent_id NOT IN (SELECT parent_fk
FROM child)
возвращает 0 записей. А запрос
SELECT * FROM parent
WHERE parent_id NOT IN (SELECT parent_fk
FROM child
WHERE parent_fk IS NOT NULL)
возвращает 1 запись как и нужно (с parent_id = 2). Получается, что в первом случае из-за присутствия NULL в результате подзапроса ниодна запись из внешнего запроса не удовлетворяет условию NOT IN ..., т.е. все записи из parent должны удовлетворять условию IN:
1 IN (NULL, 1, NULL, 3) - true
2 IN (NULL, 1, NULL, 3) - тоже получается true :)
3 IN (NULL, 1, NULL, 3) - true
Вопрос во второй выделенной строке — что за поведение? Отмечу, что тот же запрос с простым IN отрабатывает нормально, т.е. возвращает две записи (parent_id = 1 и parent_id=3):
SELECT * FROM parent
WHERE parent_id IN (SELECT parent_fk
FROM child)
Здравствуйте, sadomovalex, Вы писали:
S>Вопрос во второй выделенной строке — что за поведение?
Вероятно из:
[msdn]
...Computations involving NULL evaluate to NULL because the result
must be UNKNOWN
[/msdn]
но спасибо, что обратили внимание (мое в частности), запросы довольно популярные, можно проворонить.
Хотя (имхо конечно), лучше использовать:
SELECT * FROM parent
WHERE not exists(select parent_id from child where parent_fk = parent_id)
Здравствуйте, sadomovalex, Вы писали:
[...]
Имено эта ситуация подробно рассматривается в книге
http://www.ozon.ru/context/detail/id/1952041/ на странице 96-97:
SELECT col1 FROM Table2 WHERE col1 NOT IN (SELECT col1 FROM Table1);
определяется как
SELECT col1 FROM Table2 WHERE NOT (col1 IN (SELECT col1 FROM Table1));
Раскрывая предикат IN получим:
SELECT col1 FROM Table2 WHERE NOT (col1 = ANY (SELECT col1 FROM Table1));
Что в свою очередь означает следующее:
SELECT col1 FROM Table2 WHERE NOT ((col1=first_value_in_domain) OR (col1=second_value_in_domain) OR ... OR (col1 = NULL));
Выражение (col1 = NULL) всегда дает в результате UNKNOWN. Раскрывая по правилам Де Моргана дизъюнкции, получаем:
... WHERE ((col1<>first_value_in_domain) AND (col1<>second_value_in_domain) AND ... AND UNKNOWN)
То есть начальный запрос эквиваленен (при наличии NULL-ов в Table1.col1)
SELECT col1 FROM Table2 WHERE UNKNOWN
Этому запросу не удовлетворяет ни одна строка.