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-запрос. Это мощный инструмент для вычисляемых колонок и трансформации данных.