NULL и IS NULL / IS NOT NULL

NULL и IS NULL / IS NOT NULL

Среди всех концепций SQL NULL — пожалуй, самая неожиданная для разработчиков, пришедших из других языков программирования. В Python, JavaScript или Java null — это просто пустое значение или ссылка на «ничто». В SQL NULL — принципиально иное понятие: отсутствие информации или неизвестное значение. Это различие имеет глубокие практические последствия: NULL «заражает» вычисления, ведёт себя особенно в условиях WHERE, и его неправильное использование — частый источник трудновыявляемых ошибок.

Что такое NULL в SQL

NULL в SQL означает «значение неизвестно» или «значение не применимо». Это не ноль (0), не пустая строка (''), не false. Это отдельное специальное значение, которое сигнализирует: «мы не знаем, что здесь должно быть».

Примеры реальных сценариев, где NULL уместен:

  • Колонка middle_name (отчество) — у некоторых людей его нет
  • Колонка phone — клиент мог не указать телефон
  • Колонка discount_percent — не все товары имеют скидку
  • Колонка deleted_at — запись ещё не удалена, поле пустое

В каждом из этих случаев «нет значения» — это осмысленная информация, а не ошибка данных.

Как появляется NULL

NULL попадает в данные несколькими путями:

  • Явная вставка: INSERT INTO customers (name, phone) VALUES ('Иван', NULL)
  • Пропуск колонки при вставке, если у неё нет DEFAULT: INSERT INTO customers (name) VALUES ('Иван')phone получит NULL
  • Операции с NULL: любое выражение с NULL даёт NULL

Для просмотра: в psql NULL-значения отображаются как пустые строки или явно как NULL — зависит от настроек клиента. Можно включить явный вывод: \pset null 'NULL'.

Почему NULL = NULL не работает

Вот центральная идея: NULL означает «неизвестное». Если значение неизвестно, то сравнение «неизвестное = неизвестное» не может дать TRUE — ведь мы не знаем, одинаковы ли они. Результат такого сравнения — тоже NULL («неизвестно»).

SELECT NULL = NULL;    -- результат: NULL (не TRUE!)
SELECT NULL <> NULL;   -- результат: NULL
SELECT NULL = 0;       -- результат: NULL
SELECT NULL = '';      -- результат: NULL
SELECT NULL IS NULL;   -- результат: TRUE  ← единственный правильный способ

Из-за этого условие WHERE column = NULL никогда не возвращает строк — для всех строк, включая те, где column действительно NULL, условие дает NULL, а не TRUE. Строки с NULL-результатом условия фильтруются.

Операторы IS NULL и IS NOT NULL

Единственный правильный способ проверить на NULL:

-- Найти строки, где значение NULL (не заполнено)
SELECT * FROM customers WHERE phone IS NULL;

-- Найти строки, где значение заполнено (не NULL)
SELECT * FROM customers WHERE phone IS NOT NULL;

IS NULL и IS NOT NULL — специальные операторы именно для этой задачи. Они возвращают TRUE или FALSE, но никогда NULL.

Практический пример. Таблица employees:

id | name   | phone        | manager_id
---+--------+--------------+-----------
1  | Анна   | +7-999-1111  | NULL
2  | Борис  | NULL         | 1
3  | Вера   | +7-999-3333  | 1
4  | Гриша  | NULL         | 2

Запрос — найти сотрудников без телефона:

SELECT name FROM employees WHERE phone IS NULL;
-- Результат: Борис, Гриша

Запрос — найти руководителей верхнего уровня (без manager_id):

SELECT name FROM employees WHERE manager_id IS NULL;
-- Результат: Анна

Проверь себя: что вернёт WHERE phone = NULL для той же таблицы?

NULL «заражает» арифметику

Любая арифметическая операция с участием NULL возвращает NULL:

SELECT 5 + NULL;        -- NULL
SELECT 10 * NULL;       -- NULL
SELECT 'hello' || NULL; -- NULL (конкатенация)
SELECT price + discount -- NULL, если discount IS NULL
FROM   products;

Это «вирусное» поведение. Если в строке discount = NULL, то price + discount равно NULL, а не price. Это логично с философской точки зрения («нельзя прибавить неизвестное»), но неожиданно на практике.

Чтобы заменить NULL на конкретное значение, используется функция COALESCE:

SELECT price + COALESCE(discount, 0) AS total
FROM   products;

COALESCE(discount, 0) возвращает discount, если он не NULL, и 0 — если NULL. Это самый популярный способ обработки NULL в вычислениях.

NULL в логических операторах

Трёхзначная логика (TRUE, FALSE, NULL) применяется и в логических операторах:

TRUE  AND NULL  → NULL   (нельзя утверждать, что оба истинны)
FALSE AND NULL  → FALSE  (FALSE AND что угодно = FALSE)
TRUE  OR  NULL  → TRUE   (TRUE OR что угодно = TRUE)
FALSE OR  NULL  → NULL   (нельзя утверждать, что хоть одно истинно)
NOT NULL        → NULL   (нельзя инвертировать неизвестное)

Практическое следствие: если в WHERE есть колонка с NULL, строки, где условие дает NULL, не попадают в результат — они фильтруются, как если бы условие было FALSE.

NULL в ORDER BY

NULL-значения при сортировке: PostgreSQL по умолчанию считает NULL больше любого значения.

ORDER BY score ASC   -- NULL в конце (самые «большие»)
ORDER BY score DESC  -- NULL в начале

Управление явное: NULLS FIRST и NULLS LAST:

ORDER BY score DESC NULLS LAST    -- реальные значения по убыванию, NULL в конце
ORDER BY score ASC  NULLS FIRST   -- NULL в начале, затем значения по возрастанию

NULL и DISTINCT

DISTINCT считает все NULL одинаковыми — несколько строк с NULL в колонке схлопываются в одну:

SELECT DISTINCT phone FROM employees;
-- Если 10 строк с NULL — в результате только один NULL

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

COALESCE принимает несколько аргументов и возвращает первый не-NULL:

SELECT name,
       COALESCE(phone, 'не указан') AS display_phone
FROM   customers;

Теперь вместо пустого поля в результате будет строка «не указан».

COALESCE с несколькими аргументами полезен, когда есть несколько «запасных» значений:

SELECT COALESCE(mobile, work_phone, home_phone, 'нет телефона') AS contact
FROM   employees;

Вернёт первый не-NULL телефон из трёх вариантов, или строку «нет телефона».

NULLIF: обратная операция

NULLIF(a, b) возвращает NULL, если a = b, иначе a. Используется для обратной задачи: заменить «пустышку» на NULL. Это особенно полезно при делении, чтобы избежать деления на ноль: NULLIF(count, 0) вместо count предотвратит ошибку division by zero в выражении total / NULLIF(count, 0).

SELECT NULLIF(response_time, 0) AS cleaned_time
-- Если response_time = 0 (явно некорректное), замените на NULL

Типичные ошибки

1. WHERE column = NULL:

SELECT * FROM orders WHERE discount = NULL;   -- всегда 0 строк
SELECT * FROM orders WHERE discount IS NULL;  -- правильно

2. NOT IN со списком, содержащим NULL:

-- Если orders.user_id может быть NULL — запрос вернёт 0 строк!
WHERE id NOT IN (SELECT user_id FROM orders)
-- Безопасно:
WHERE id NOT IN (SELECT user_id FROM orders WHERE user_id IS NOT NULL)

3. Арифметика без COALESCE:

SELECT price + shipping_cost AS total   -- NULL, если shipping_cost IS NULL
-- Безопасно:
SELECT price + COALESCE(shipping_cost, 0) AS total

Краткий итог

  • NULL означает «неизвестное значение», а не ноль и не пустую строку
  • NULL = NULL даёт NULL, а не TRUE — используйте IS NULL и IS NOT NULL
  • Любая арифметика с NULL возвращает NULL; COALESCE заменяет NULL на значение по умолчанию
  • В логических операторах NULL распространяется («заражает»), кроме случаев FALSE AND NULL = FALSE и TRUE OR NULL = TRUE
  • ORDER BY помещает NULL в конец при ASC; управляйте через NULLS FIRST / LAST

Что дальше

Вы разобрались с NULL — одним из самых важных концептов SQL. Последний специальный оператор этого модуля — CASE WHEN, который позволяет добавить логику ветвления прямо в SQL-запрос. Это мощный инструмент для вычисляемых колонок и трансформации данных.

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

Практические задачи, квизы и AI-наставник — бесплатный старт без карты

Перейти к практике