🛠️ Разбор мини-лабы.

Подзапрос возвращает не только 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 и аналитике данных такие ловушки разбираются на маленьких наборах данных: так проще увидеть, где ломается логика отчёта и почему корректный на вид запрос возвращает пустой результат.

🔹🔹🔹🔹

🛠️ Разбор мини-лабы | Сетка — социальная сеть от hh.ru