Почему NOT IN может неожиданно вернуть 0 строк? Есть таблица клиентов: customer

1 | Иван 2 | Анна 3 | Максим И таблица заблокированных клиентов: blocked_customer

2 NULL Нужно получить всех клиентов, которых нет в списке блокировки. Кажется логичным: SELECT * FROM customer WHERE id NOT IN ( SELECT customer_id FROM blocked_customer ); Ожидаем: Иван Максим А PostgreSQL может вернуть: 0 строк Почему?

Всё из-за NULL Условие фактически превращается примерно в: id <> 2 AND id <> NULL Но сравнение: id <> NULL не даёт TRUE или FALSE. Результат: UNKNOWN А WHERE оставляет только строки, для которых условие получилось: TRUE Поэтому наличие одного NULL внутри NOT IN может сломать весь ожидаемый результат.

Как сделать надёжнее? Часто лучше использовать NOT EXISTS: SELECT c.* FROM customer c WHERE NOT EXISTS ( SELECT 1 FROM blocked_customer b WHERE b.customer_id = c.id ); Теперь логика звучит буквально: Верни клиента, если для него не существует записи в blocked_customer. Результат: 1 | Иван 3 | Максим NULL внутри таблицы блокировок этому не мешает.

А можно оставить NOT IN? Можно, если явно исключить NULL: SELECT * FROM customer WHERE id NOT IN ( SELECT customer_id FROM blocked_customer WHERE customer_id IS NOT NULL ); Но здесь появляется важный вопрос: Почему customer_id вообще допускает NULL, если по бизнес-смыслу заблокированная запись обязана относиться к конкретному клиенту? Иногда правильная оптимизация начинается не с SQL, а со структуры данных: customer_id BIGINT NOT NULL🤓

#sql #postgresql #базы_данных