NULL в SQL: шпаргалка для собеседования аналитика
Зачем аналитику знать NULL
NULL — самая частая причина «странных» результатов в SQL. Поле которого было 100 строк, после фильтра стало 50 — и виноват NULL.
На собесе задачи про NULL — способ проверить, вы действительно разбираетесь в SQL или просто зазубрили синтаксис. Самые типовые ловушки — про ответы в WHERE, агрегатах и JOIN.
Главное про NULL
NULL — это не значение. Это отсутствие значения. Отсюда все странности:
NULL = NULL → UNKNOWN (не TRUE!)
NULL != NULL → UNKNOWN
NULL + 1 → NULL
'abc' = NULL → UNKNOWN
NULL OR TRUE → TRUE
NULL AND TRUE → UNKNOWN
NULL OR FALSE → UNKNOWN
NULL AND FALSE → FALSEПравило: любая арифметика или сравнение с NULL → NULL (или UNKNOWN в булевой логике).
Проверка на NULL — IS NULL / IS NOT NULL
-- ✅ Правильно
SELECT * FROM users WHERE email IS NULL;
SELECT * FROM users WHERE email IS NOT NULL;
-- ❌ Не сработает — всегда вернёт 0 строк
SELECT * FROM users WHERE email = NULL;
SELECT * FROM users WHERE email != NULL;email = NULL возвращает UNKNOWN для любой строки, WHERE оставляет только TRUE — значит, 0 строк.
COALESCE — «первое не-NULL»
-- Возвращает первое не-NULL значение
SELECT COALESCE(phone, email, 'unknown') AS contact
FROM users;
-- Заполнить пропуски в агрегате
SELECT user_id, COALESCE(SUM(amount), 0) AS revenue
FROM orders
RIGHT JOIN users USING (user_id)
GROUP BY user_id;COALESCE — главный инструмент работы с NULL в аналитике.
NULLIF — превращаем значение в NULL
-- Если знаменатель 0, вернём NULL (избежим деления на 0)
SELECT revenue / NULLIF(users, 0) AS arpu FROM stats;NULLIF(a, b) возвращает NULL если a = b, иначе a. Классический трюк для защиты от division by zero.
Больше таких примеров с разборами — в Telegram-тренажёре. Короткие сессии, прогресс по темам, объяснения после каждого ответа.
NULL в агрегатах
Все агрегаты (кроме COUNT(*)) игнорируют NULL:
-- Дано: [10, 20, NULL, 30]
SELECT
COUNT(*), -- 4 (все строки)
COUNT(amount), -- 3 (игнорирует NULL)
SUM(amount), -- 60
AVG(amount), -- 20 (60/3, а не 60/4!)
MIN(amount), -- 10
MAX(amount) -- 30
FROM orders;Ловушка на собесе: «Почему AVG даёт 20, а не 15?» — потому что NULL не участвует в расчёте. Если нужно считать NULL как 0 — AVG(COALESCE(amount, 0)).
NULL в WHERE
Любое условие с NULL возвращает UNKNOWN, и строка не попадает в результат:
-- Это исключит строки с email = NULL
SELECT * FROM users WHERE email != 'admin@example.com';Чтобы включить и NULL:
SELECT * FROM users WHERE email != 'admin@example.com' OR email IS NULL;NULL в JOIN
INNER JOIN
Строки с NULL в ключе не матчатся — даже между собой:
-- users.manager_id=NULL не соединится ни с чем
SELECT * FROM users u INNER JOIN users m ON u.manager_id = m.id;LEFT JOIN
Левая таблица сохраняется, в правой NULL:
SELECT u.name, o.order_id
FROM users u
LEFT JOIN orders o ON o.user_id = u.user_id;
-- Для пользователей без заказов: order_id = NULLANTI JOIN через LEFT JOIN + IS NULL
Найти пользователей БЕЗ заказов:
SELECT u.*
FROM users u
LEFT JOIN orders o ON o.user_id = u.user_id
WHERE o.user_id IS NULL;NULL и NOT IN — главная ловушка
-- Если подзапрос возвращает NULL, весь NOT IN даст 0 строк
SELECT * FROM users
WHERE user_id NOT IN (SELECT referred_by FROM users);
-- referred_by может быть NULL → весь фильтр даёт 0 строкПравильно:
-- Вариант 1: исключить NULL
SELECT * FROM users
WHERE user_id NOT IN (
SELECT referred_by FROM users WHERE referred_by IS NOT NULL
);
-- Вариант 2: NOT EXISTS (безопасно по умолчанию)
SELECT * FROM u1
WHERE NOT EXISTS (SELECT 1 FROM u2 WHERE u2.referred_by = u1.user_id);NULL в GROUP BY
Все NULL попадают в одну группу:
SELECT category, COUNT(*) FROM products GROUP BY category;
-- Одна из строк будет category = NULLЕсли хотите явно — оберните в COALESCE:
SELECT COALESCE(category, 'unknown') AS category, COUNT(*) FROM products GROUP BY 1;NULL в DISTINCT
DISTINCT оставляет ровно один NULL:
-- Дано: ['a', NULL, 'b', NULL, 'a']
SELECT DISTINCT col FROM t;
-- Результат: ['a', 'b', NULL]Если готовишься к собесу — бот @kariernik_bot закрывает 80% технических вопросов. SQL, Python, A/B, продуктовые метрики — всё в одном месте.
NULL в ORDER BY
По умолчанию NULL идут в конце (ASC) или в начале (DESC) в большинстве СУБД. Можно явно:
SELECT * FROM orders ORDER BY shipped_at NULLS FIRST;
SELECT * FROM orders ORDER BY shipped_at NULLS LAST;Конкатенация строк с NULL
-- PostgreSQL: || с NULL даёт NULL
SELECT first_name || ' ' || last_name FROM users;
-- Если last_name = NULL, результат = NULL
-- Решение — CONCAT (игнорирует NULL)
SELECT CONCAT(first_name, ' ', last_name) FROM users;
-- Или COALESCE
SELECT COALESCE(first_name, '') || ' ' || COALESCE(last_name, '') FROM users;10 задач с собесов
1. Заменить NULL в amount на 0
SELECT COALESCE(amount, 0) FROM orders;2. Найти строки, где email пустой
SELECT * FROM users WHERE email IS NULL OR email = '';Важно: пустая строка '' — это не NULL.
3. Безопасное деление (не делить на 0)
SELECT revenue / NULLIF(users_count, 0) AS arpu FROM stats;4. Пользователи без заказов
SELECT u.* FROM users u
LEFT JOIN orders o ON o.user_id = u.user_id
WHERE o.user_id IS NULL;5. Количество пользователей с телефоном (не NULL)
SELECT COUNT(phone) AS with_phone, COUNT(*) AS total FROM users;6. Средний чек, NULL считается как 0
SELECT AVG(COALESCE(amount, 0)) FROM orders;7. Заказы, где статус не 'canceled'
-- ✅ Включаем NULL в статусе
SELECT * FROM orders WHERE status != 'canceled' OR status IS NULL;8. Топ пользователей по выручке (NULL внизу)
SELECT user_id, SUM(amount) FROM orders GROUP BY user_id
ORDER BY 2 DESC NULLS LAST;9. Подсчёт заказов с оплатой и без
SELECT
COUNT(*) FILTER (WHERE paid_at IS NOT NULL) AS paid,
COUNT(*) FILTER (WHERE paid_at IS NULL) AS unpaid
FROM orders;10. Процент NULL в колонке
SELECT
COUNT(*) FILTER (WHERE col IS NULL) * 100.0 / COUNT(*) AS null_pct
FROM t;Как тренироваться
Работу с NULL учат через ошибки. Напишите запрос без IS NULL, потом посмотрите, почему фильтр не сработал — и запомните на всю жизнь.
Тренажёр Карьерник содержит блок задач на NULL: COALESCE, NULLIF, NOT IN ловушки, LEFT JOIN + IS NULL для ANTI JOIN. Каждая с разбором.
Совет: на собесе, видя любое сравнение с возможным NULL, сразу проговаривайте вслух: «если тут может быть NULL, результат может быть NULL». Это заметно — и ценится.
Читайте также
- NULL в SQL: подробный гайд
- COALESCE в SQL: использование и примеры
- EXISTS vs IN в SQL
- JOIN SQL: шпаргалка
FAQ
Чем NULL отличается от пустой строки?
NULL — отсутствие значения, пустая строка '' — это значение с длиной 0. NULL != ''. LENGTH('') = 0, LENGTH(NULL) = NULL. В реальных данных часто встречаются обе и нужно явно их различать.
Почему WHERE col != 'x' не включает строки с NULL?
Потому что NULL != 'x' даёт UNKNOWN, а WHERE оставляет только TRUE. Решение: WHERE col != 'x' OR col IS NULL.
Как работает SUM с NULL?
Игнорирует — суммирует только не-NULL значения. Если все значения NULL, SUM вернёт NULL (не 0!). COALESCE(SUM(col), 0) — защита от этого.
IS NULL или = NULL?
IS NULL. = NULL всегда даёт UNKNOWN. На собесе это первый вопрос на владение SQL, если человек говорит = NULL — минус балл.