Как работать с NULL в SQL
Что проверяют
Понимает ли кандидат, что NULL это не значение, а маркер отсутствия, и что сравнение с ним даёт не TRUE или FALSE, а UNKNOWN. Интервьюер хочет услышать про трёхзначную логику и типовые ловушки в WHERE, JOIN, агрегатах и NOT IN.
Как отвечать
NULL означает «значение неизвестно». Это не пустая строка и не ноль: '' = NULL и 0 = NULL возвращают не FALSE, а UNKNOWN.
Отсюда трёхзначная логика: TRUE, FALSE, UNKNOWN. WHERE пропускает строку только при TRUE, поэтому и col = NULL, и col <> 'x' для строки с NULL не вернут ничего. Проверка только через IS NULL / IS NOT NULL, а для сравнения двух возможно пустых значений есть IS NOT DISTINCT FROM в PostgreSQL и <=> в MySQL.
Подстановка значений:
SELECT COALESCE(email, 'нет адреса') AS email, -- первое не-NULL из списка
NULLIF(divisor, 0) AS safe_divisor -- обратная операция
FROM users;Агрегаты:
SUM,AVG,MIN,MAXигнорируютNULL, то естьAVGсчитает по меньшему числу строк, и это не то же самое, что среднее с нулями;COUNT(*)считает все строки,COUNT(col)только строки с не-NULL;SUMпо пустому набору возвращаетNULL, а не 0, поэтому в отчётах пишутCOALESCE(SUM(x), 0).
Где NULL ведёт себя не как в WHERE: GROUP BY и DISTINCT считают все NULL одинаковыми и схлопывают их в одну группу. В ORDER BY порядок зависит от СУБД (в PostgreSQL при ASC они последние), управляется через NULLS FIRST / NULLS LAST. Уникальный индекс обычно допускает много NULL, в PostgreSQL 15+ это меняется опцией UNIQUE NULLS NOT DISTINCT.
Две классические ловушки:
NOT IN (подзапрос)возвращает пустой результат целиком, если в подзапросе встретился хотя бы одинNULL. Лечится черезNOT EXISTS;- после
LEFT JOINусловие на правую таблицу, вынесенное вWHERE, отбрасывает несовпавшие строки и превращает соединение во внутреннее. Такое условие должно жить вON.
На проектировании: ставить NOT NULL с DEFAULT там, где отсутствие данных невозможно по смыслу, и оставлять NULL там, где отсутствие содержательно (дата увольнения у работающего сотрудника). Заменять его на -1 или пустую строку хуже: такие значения молча попадают в агрегаты.
Где спотыкаются
Пишут = NULL и ждут, что <> вернёт строки с пустыми значениями, а расхождение отчётов объясняют чем угодно, кроме трёхзначной логики. Второе частое место: уверенность, что NOT IN работает так же, как NOT EXISTS.