NULL в SQL — что это и как с ним работать
Коротко
NULL в SQL — это отсутствие значения. Не ноль, не пустая строка, не false. NULL означает «неизвестно». Это фундаментальная концепция, без которой невозможно писать корректные запросы — и частая тема на собеседованиях аналитика данных.
Что такое NULL
NULL — это маркер, обозначающий, что значение отсутствует или неизвестно. Важно понимать три вещи:
NULL ≠ 0— ноль это конкретное число, NULL это отсутствие числаNULL ≠ ''— пустая строка это строка длиной 0, NULL это отсутствие строкиNULL ≠ FALSE— false это логическое значение, NULL это неизвестность
-- Все три значения разные
SELECT
CASE WHEN NULL = 0 THEN 'равны' ELSE 'не равны' END, -- не равны
CASE WHEN NULL = '' THEN 'равны' ELSE 'не равны' END, -- не равны
CASE WHEN NULL = NULL THEN 'равны' ELSE 'не равны' END; -- не равны!Последняя строка — ключевая. NULL = NULL не возвращает TRUE. Потому что два неизвестных значения не обязаны быть одинаковыми.
Трёхзначная логика
В обычной логике есть TRUE и FALSE. В SQL есть третье значение — UNKNOWN. Любое сравнение с NULL возвращает UNKNOWN:
| Выражение | Результат |
|---|---|
| NULL = NULL | UNKNOWN |
| NULL <> NULL | UNKNOWN |
| NULL > 5 | UNKNOWN |
| NULL AND TRUE | UNKNOWN |
| NULL OR TRUE | TRUE |
| NULL OR FALSE | UNKNOWN |
| NOT NULL | UNKNOWN |
WHERE отбирает только строки, где условие = TRUE. Строки с UNKNOWN отфильтровываются так же, как FALSE.
IS NULL и IS NOT NULL
Для проверки на NULL нельзя использовать =. Используйте IS NULL и IS NOT NULL:
-- Правильно
SELECT * FROM users WHERE email IS NULL;
-- Неправильно — вернёт 0 строк всегда!
SELECT * FROM users WHERE email = NULL;WHERE email = NULL превращается в UNKNOWN для каждой строки, и ничего не проходит фильтр. Это классическая ошибка новичков.
NULL в агрегатных функциях
Агрегатные функции обрабатывают NULL по-разному:
| Функция | Поведение с NULL |
|---|---|
| COUNT(*) | Считает все строки, включая NULL |
| COUNT(column) | Пропускает NULL |
| SUM(column) | Игнорирует NULL |
| AVG(column) | Игнорирует NULL (не учитывает в знаменателе) |
| MIN / MAX | Игнорируют NULL |
-- Таблица bonuses: {1000, NULL, 2000, NULL, 3000}
SELECT
COUNT(*) AS total_rows, -- 5
COUNT(amount) AS non_null_count, -- 3
SUM(amount) AS total, -- 6000
AVG(amount) AS average -- 2000 (6000/3, не 6000/5!)
FROM bonuses;AVG игнорирует NULL — делит сумму на количество не-NULL значений. Если вам нужно считать NULL как 0, оберните в COALESCE: AVG(COALESCE(amount, 0)).
COALESCE и NULLIF
COALESCE возвращает первое не-NULL значение из списка аргументов. Используется для подстановки значений по умолчанию:
SELECT
user_id,
COALESCE(nickname, first_name, 'Аноним') AS display_name
FROM users;NULLIF(a, b) — обратная операция: возвращает NULL, если a = b, иначе a. Классическое применение — защита от деления на ноль:
-- Без NULLIF: деление на ноль = ошибка
SELECT revenue / sessions AS cps FROM metrics;
-- С NULLIF: если sessions = 0, вернёт NULL вместо ошибки
SELECT revenue / NULLIF(sessions, 0) AS cps FROM metrics;Подробнее о разнице между COALESCE и ISNULL — в отдельном посте.
NULL в JOIN
При JOIN строки не соединяются по NULL. NULL = NULL → UNKNOWN, а JOIN требует TRUE:
-- Строки с department = NULL не попадут в результат
SELECT *
FROM employees e
JOIN departments d ON e.department_id = d.id;Если в employees есть записи с department_id = NULL, они не соединятся ни с одной строкой из departments — даже если в departments тоже есть NULL. Используйте LEFT JOIN, если хотите сохранить такие строки.
Ловушка NOT IN с NULL
Это самая опасная ловушка, связанная с NULL. Если подзапрос в NOT IN возвращает хотя бы один NULL, результат — пустая таблица:
-- Подзапрос возвращает: {1, 2, NULL}
SELECT * FROM orders
WHERE customer_id NOT IN (SELECT id FROM blacklist);
-- Результат: 0 строк!Почему? customer_id NOT IN (1, 2, NULL) раскрывается в customer_id <> 1 AND customer_id <> 2 AND customer_id <> NULL. Последнее условие всегда UNKNOWN, а TRUE AND UNKNOWN = UNKNOWN. Ни одна строка не проходит.
Решение — используйте NOT EXISTS вместо NOT IN:
SELECT * FROM orders o
WHERE NOT EXISTS (
SELECT 1 FROM blacklist b WHERE b.id = o.customer_id
);NULL в ORDER BY
По умолчанию NULL считается «неизвестным» и может оказаться в начале или в конце — зависит от СУБД. В PostgreSQL NULL идёт последним при ASC и первым при DESC. Управлять можно явно:
SELECT * FROM users
ORDER BY last_login NULLS LAST;
SELECT * FROM users
ORDER BY last_login DESC NULLS FIRST;Типичные ошибки
- = NULL вместо IS NULL — самая частая ошибка.
WHERE col = NULLне работает - NOT IN с подзапросом, содержащим NULL — возвращает 0 строк. Используйте NOT EXISTS
- Ожидание 0 вместо NULL — SUM пустого набора возвращает NULL, не 0. Оберните в COALESCE
- AVG с NULL — NULL-строки не учитываются в знаменателе. Это может исказить среднее
Вопросы с собеседований
— Что такое NULL в SQL? — NULL — это маркер отсутствия значения. Не ноль, не пустая строка. Любое сравнение с NULL возвращает UNKNOWN, поэтому для проверки используют IS NULL / IS NOT NULL.
— Что вернёт NULL = NULL?
— UNKNOWN (не TRUE и не FALSE). Два неизвестных значения не считаются равными. Для сравнения двух значений с учётом NULL в PostgreSQL есть IS NOT DISTINCT FROM.
— Чем COUNT(*) отличается от COUNT(column)? — COUNT(*) считает все строки. COUNT(column) пропускает строки, где column = NULL. Подробнее — в отдельном разборе.
— Почему NOT IN с NULL возвращает пустой результат?
— Потому что x <> NULL = UNKNOWN, а UNKNOWN в AND превращает всё выражение в UNKNOWN. Ни одна строка не проходит WHERE. Безопасная альтернатива — NOT EXISTS.
— Как заменить NULL на значение по умолчанию? — COALESCE(column, default_value). Например, COALESCE(discount, 0) вернёт 0 вместо NULL.
— Как NULL ведёт себя в JOIN? — Строки не соединяются по NULL, потому что NULL = NULL → UNKNOWN, а JOIN требует TRUE. Строки с NULL в ключе соединения «пропадают» при INNER JOIN.
Потренируйтесь на реальных вопросах с собеседований — откройте тренажёр. Там 1500+ вопросов по SQL и другим темам аналитика, включая примеры вопросов по NULL, агрегатам и джойнам.
FAQ
NULL и пустая строка — это одно и то же?
Нет. Пустая строка ('') — это конкретное значение длиной 0. NULL — отсутствие значения. '' IS NULL вернёт FALSE (исключение — Oracle, где пустая строка = NULL, но это особенность конкретной СУБД).
Как посчитать количество NULL в колонке?
COUNT(*) - COUNT(column). COUNT(*) считает все строки, COUNT(column) только не-NULL. Разница — количество NULL.
COALESCE влияет на производительность?
Сам по себе — нет, это простая операция. Но если обернуть индексированный столбец — WHERE COALESCE(col, 0) > 10 — СУБД не сможет использовать индекс. Лучше писать WHERE col > 10 OR col IS NULL.
Можно ли использовать NULL как значение по умолчанию в столбце?
Да, и это поведение по умолчанию, если не указан NOT NULL или DEFAULT. Если столбец допускает NULL и при INSERT значение не передано — запишется NULL. Используйте SQL-тренажёр для практики.