🛠️ Разбор мини-лабы.
Подзапрос возвращает не только 1 и 2, но и NULL:
1 2 NULL
Для клиента Вера условие фактически превращается в проверку:
3 NOT IN (1, 2, NULL)
Интуитивно хочется прочитать это так:
3 не равен 1 и 3 не равен 2 и 3 не равен NULL
Первые две части понятны:
3 <> 1 → TRUE 3 <> 2 → TRUE
А третья часть не становится TRUE:
3 <> NULL → UNKNOWN
В SQL NULL означает отсутствие известного значения. Поэтому обычное сравнение с NULL даёт не TRUE и не FALSE, а UNKNOWN / NULL.
Итог:
TRUE AND TRUE AND UNKNOWN → UNKNOWN
А строка проходит через WHERE только тогда, когда условие стало TRUE.
Поэтому клиенты Вера и Дмитрий не попали в результат.
Как исправить?
Вариант 1 — использовать NOT EXISTS:
SELECT c.client_id, c.name FROM clients c WHERE NOT EXISTS ( SELECT 1 FROM orders o WHERE o.client_id = c.client_id );
Результат:
3 Вера 4 Дмитрий
Вариант 2 — оставить NOT IN, но убрать NULL из подзапроса:
SELECT c.client_id, c.name FROM clients c WHERE c.client_id NOT IN ( SELECT o.client_id FROM orders o WHERE o.client_id IS NOT NULL );
Результат будет тем же:
3 Вера 4 Дмитрий
NOT IN не плохой сам по себе. Но если подзапрос может вернуть NULL, результат может оказаться неожиданным.
Практический вывод: перед NOT IN с подзапросом проверьте, может ли правая часть вернуть NULL. Для такой задачи часто безопаснее использовать NOT EXISTS или явно отфильтровать NULL, если строки с NULL не должны участвовать в проверке.
На курсах по SQL и аналитике данных такие ловушки разбираются на маленьких наборах данных: так проще увидеть, где ломается логика отчёта и почему корректный на вид запрос возвращает пустой результат.
🔹🔹🔹🔹