Авторский канал про Базы Данных и SQL
Ресурсы, гайды, задачи, шпаргалки.
Информация ежедневно пополняется!
Cотрудничество: @energy_c
РКН: https://clck.ru/3QREBc
Post #1957
2.39K
Почему NOT IN может вернуть пустой результат из-за NULL!
Одна из самых неприятных ловушек SQL — поведение
Таблицы:
Допустим, нужно получить пользователей, которых нет в
Пока в
Теперь запрос внезапно вернет:
Хотя пользователи без бана есть. Почему так происходит — SQL сравнивает условие примерно как:
Но:
не даёт TRUE или FALSE. Результат: UNKNOWN. А в
Это особенность трёхзначной логики SQL: TRUE, FALSE, UNKNOWN
Любое сравнение с NULL даёт UNKNOWN:
Поэтому
Безопасный вариант —
Почему
Ещё вариант — явно убрать NULL:
Но на практике:
Отдельный момент:
Быстрая проверка проблемы:
Если такие строки есть —
🔥
➡️ SQL Ready | #практика
Одна из самых неприятных ловушек SQL — поведение
NOT IN при наличии NULL. Запрос выглядит абсолютно корректно, но внезапно перестаёт возвращать строки.Таблицы:
users(id)
bans(user_id)
Допустим, нужно получить пользователей, которых нет в
bans. Часто пишут так:SELECT *
FROM users
WHERE id NOT IN (
SELECT user_id
FROM bans
);
Пока в
bans.user_id нет NULL — всё работает нормально. Но представим данные:bans
-------
1
2
NULL
Теперь запрос внезапно вернет:
0 rows
Хотя пользователи без бана есть. Почему так происходит — SQL сравнивает условие примерно как:
id <> 1
AND id <> 2
AND id <> NULL
Но:
id <> NULL
не даёт TRUE или FALSE. Результат: UNKNOWN. А в
WHERE проходят только TRUE. Из-за этого всё условие целиком перестаёт выполняться.Это особенность трёхзначной логики SQL: TRUE, FALSE, UNKNOWN
Любое сравнение с NULL даёт UNKNOWN:
NULL = 1
NULL <> 1
NULL = NULL
Поэтому
NOT IN с NULL внутри подзапроса становится опасным.Безопасный вариант —
NOT EXISTS:SELECT *
FROM users u
WHERE NOT EXISTS (
SELECT 1
FROM bans b
WHERE b.user_id = u.id
);
Почему
EXISTS работает нормально: сравнение идёт построчно; NULL не ломает всю проверку; оптимизатор обычно хорошо превращает это в anti-join.Ещё вариант — явно убрать NULL:
SELECT *
FROM users
WHERE id NOT IN (
SELECT user_id
FROM bans
WHERE user_id IS NOT NULL
);
Но на практике:
NOT EXISTS обычно считается более безопасным и читаемым решением.Отдельный момент:
NOT IN () и NOT EXISTS могут давать разные планы выполнения в зависимости от СУБД. Но логически для nullable данных: NOT EXISTS почти всегда предпочтительнее.Быстрая проверка проблемы:
SELECT COUNT(*)
FROM bans
WHERE user_id IS NULL;
Если такие строки есть —
NOT IN уже потенциально опасен.🔥
NOT IN и NULL плохо сочетаются. Если в подзапросе появляется хотя бы один NULL, условие может перестать возвращать строки вообще. Для таких проверок надёжнее использовать NOT EXISTS.➡️ SQL Ready | #практика
- 👍 22
- ❤ 10
- 🔥 5
- 🤝 1













