SQLLab
Все статьи

NULL в SQL: ловушки, IS NULL, COALESCE и NULLIF

Как работает NULL в SQL: сравнение, агрегатные функции, JOIN с NULL, COALESCE и NULLIF. Частые ошибки новичков и способы их избежать.

7 марта 2026 г.·5 мин чтения·

NULL в SQL — не ноль и не пустая строка. Это отсутствие значения. И именно из-за непонимания этого факта новички делают ошибки, которые трудно отловить.

NULL ≠ 0 и NULL ≠ ''

SELECT 0 = 0;      -- TRUE
SELECT '' = '';    -- TRUE
SELECT NULL = NULL; -- NULL (не TRUE!)

Любое сравнение с NULL возвращает NULL — не TRUE и не FALSE. Это называется трёхзначная логика (Three-Valued Logic): TRUE, FALSE, NULL.

Как правильно проверять NULL

-- ❌ Так не работает
SELECT * FROM users WHERE phone = NULL;   -- вернёт 0 строк всегда
SELECT * FROM users WHERE phone != NULL;  -- тоже 0 строк

-- ✅ Правильно
SELECT * FROM users WHERE phone IS NULL;
SELECT * FROM users WHERE phone IS NOT NULL;

NULL в логических выражениях

ВыражениеРезультат
NULL AND TRUENULL
NULL AND FALSEFALSE
NULL OR TRUETRUE
NULL OR FALSENULL
NOT NULLNULL

NULL AND FALSE = FALSE — потому что неважно, что второй операнд: результат всегда FALSE. Это единственный случай, когда NULL не «заражает» выражение.

NULL в агрегатных функциях

COUNT(*) считает все строки, включая NULL. COUNT(col) — только строки, где col не NULL.

SELECT
    COUNT(*) AS total_rows,          -- 100
    COUNT(phone) AS with_phone,      -- 73 (только не NULL)
    AVG(salary) AS avg_salary        -- игнорирует NULL
FROM employees;

Важно: AVG, SUM, MIN, MAX — все игнорируют NULL. Это может исказить результат, если NULL обозначает «0».

-- Если NULL = отсутствие продаж (= 0), а не пропущенные данные:
AVG(COALESCE(sales, 0))   -- вместо AVG(sales)

COALESCE: замена NULL на значение по умолчанию

-- Первый не-NULL аргумент
COALESCE(a, b, c, ...)
SELECT
    name,
    COALESCE(phone, email, 'Нет контактов') AS contact
FROM users;
-- ❌ COALESCE здесь не поможет — деление на ноль всё равно произойдёт
SELECT revenue / COALESCE(visits, 0) AS revenue_per_visit  -- ERROR: division by zero

-- ✅ NULLIF вернёт NULL вместо 0, и деление даст NULL (безопасно)
SELECT revenue / NULLIF(visits, 0) AS revenue_per_visit
FROM stats;

Почему COALESCE(visits, 0) не работает: он заменяет NULL на 0, но если visits уже равен 0 (не NULL), деление всё равно произойдёт. NULLIF(visits, 0) же превращает именно ноль в NULL — что и нужно.

В разных СУБД есть свои аналоги COALESCE: IFNULL(a, b) в MySQL, ISNULL(a, b) в SQL Server, NVL(a, b) в Oracle. Но все они принимают только два аргумента. COALESCE — стандартный SQL и принимает любое количество аргументов. С точки зрения производительности разницы практически нет — оптимизаторы обрабатывают их одинаково.

NULLIF: превращаем значение в NULL

NULLIF(a, b)  -- вернёт NULL если a = b, иначе вернёт a
-- Классика: защита от деления на ноль
SELECT amount / NULLIF(quantity, 0) AS unit_price
FROM items;

-- Убрать пустые строки как NULL
SELECT NULLIF(TRIM(comment), '') AS comment
FROM reviews;

NULL в JOIN

CREATE TABLE a (id INT, val TEXT);
CREATE TABLE b (id INT, val TEXT);

INSERT INTO a VALUES (1, 'x'), (NULL, 'y');
INSERT INTO b VALUES (1, 'p'), (NULL, 'q');
SELECT * FROM a JOIN b ON a.id = b.id;
-- Результат: только строка (1, 'x') — (1, 'p')
-- NULL JOIN NULL не совпадает!

NULL никогда не равен NULL в условиях JOIN. Если нужно соединить по NULL:

SELECT * FROM a JOIN b ON a.id IS NOT DISTINCT FROM b.id;
-- IS NOT DISTINCT FROM обрабатывает NULL = NULL как TRUE

NULL в ORDER BY

-- По умолчанию в PostgreSQL NULL идут последними при ASC
SELECT name, score FROM results ORDER BY score ASC;

-- Можно явно управлять
ORDER BY score ASC NULLS FIRST;   -- NULL в начале
ORDER BY score DESC NULLS LAST;   -- NULL в конце

NULL в UNIQUE ограничении

В большинстве СУБД (PostgreSQL, MySQL) несколько NULL в уникальном столбце — разрешены. NULL ≠ NULL, поэтому конфликта нет. Но в SQL Server — иначе, допускается только один NULL.

Типичные ловушки на собеседовании

Сколько строк вернёт запрос?

SELECT * FROM users WHERE age != 25;

Строки где age IS NULL не попадут в результат! Чтобы включить:

WHERE age != 25 OR age IS NULL;

Что выведет этот запрос?

SELECT 1 + NULL;          -- NULL
SELECT CONCAT('hi', NULL); -- NULL (в большинстве СУБД)
SELECT 'hi' || NULL;      -- NULL в PostgreSQL
-- Исключение: в Oracle конкатенация 'hi' || NULL вернёт 'hi' — там NULL в строках игнорируется

Что вернёт COUNT при всех NULL?

SELECT COUNT(col) FROM t;  -- 0 (все NULL игнорируются)
SELECT COUNT(*) FROM t;    -- N (количество строк)

Итого

  • Проверяй NULL только через IS NULL / IS NOT NULL
  • NULL «заражает» арифметику и сравнения — результат тоже NULL
  • Агрегаты игнорируют NULL — думай, это правильно для твоей задачи?
  • В JOIN строки с NULL не совпадают — используй IS NOT DISTINCT FROM если нужно
  • COALESCE — для подстановки дефолта, NULLIF — для создания NULL из значения

Похожие статьи

Попробуй на практике

Тренажёр с реальными задачами — бесплатно и без регистрации

Открыть тренажёр →