Программирование SQLDML и запросыМладший SQL-разработчик

Как отличить проверку столбца на NULL от сравнения его с NULL в условии отбора?

Как отличить проверку столбца на NULL от сравнения его с NULL в условии отбора?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

NULL означает неизвестное или отсутствующее значение, поэтому обычное сравнение с ним не возвращает TRUE: результатом становится UNKNOWN. Для проверки наличия NULL применяют предикаты IS NULL и IS NOT NULL; в условие WHERE проходят только строки, для которых результат равен TRUE.

WITH data(value) AS ( VALUES (1), (NULL) ) SELECT value, value = NULL AS ordinary_comparison, value IS NULL AS null_check FROM data;

В первой строке обычное сравнение с NULL даёт UNKNOWN, а во второй проверка IS NULL — TRUE. Поэтому сравнение с NULL не заменяет специальную проверку.

Исторический контекст

В SQL потребовалось отличать известное значение от отсутствующего или неизвестного. Для этого используется NULL, но он не является обычным значением вроде числа или строки: он обозначает отсутствие известного значения.

Из-за этого логика SQL стала трёхзначной: результат логического выражения может быть TRUE, FALSE или UNKNOWN. Такой подход позволяет не делать ложный вывод о равенстве или неравенстве, когда фактическое значение неизвестно.

Постановка проблемы

Представим фильтр, который должен найти строки без даты удаления. Если разработчик сравнивает столбец с NULL обычным оператором равенства, строки с пропущенной датой не попадут в результат.

Это опасно для очистки данных, отчётов и DML-операций. Например, UPDATE или DELETE с таким условием может не изменить и не удалить ожидаемые строки, хотя запрос синтаксически корректен.

Подробное решение

Операторы сравнения, такие как равенство, не считают NULL равным NULL. Даже если оба операнда содержат NULL, результат сравнения — UNKNOWN, потому что SQL не может утверждать, что два неизвестных значения одинаковы.

Предикат IS NULL специально проверяет отсутствие значения и возвращает TRUE именно для NULL. Предикат IS NOT NULL возвращает TRUE для известных значений.

В WHERE сохраняются только строки с результатом TRUE. Строки с FALSE и UNKNOWN отбрасываются, поэтому выражение с обычным сравнением с NULL фактически не выбирает строки с NULL.

Это влияет и на составные условия. Например, TRUE AND UNKNOWN даёт UNKNOWN, а FALSE AND UNKNOWN даёт FALSE. Поэтому добавление проверки NULL в сложное условие может изменить результат не так, как предполагает интуитивная двухзначная логика.

Если нужно сравнивать два значения так, чтобы NULL считался равным NULL, используют диалектный предикат IS NOT DISTINCT FROM либо явно описывают оба случая через IS NULL и обычное сравнение. Поддержка и синтаксис такого предиката зависят от СУБД, поэтому для переносимого SQL обычно применяют явную проверку.

Ситуация из практики

В таблице заказов поле даты отмены допускает NULL. Требовалось выбрать активные заказы, но фильтр через обычное сравнение с NULL вернул пустой результат.

Рассматривались два варианта. Использовать сравнение с NULL было проще по виду, но оно давало UNKNOWN и не выбирало строки. Применить IS NULL было явно и переносимо, однако при добавлении дополнительных условий требовалось отдельно проверить их взаимодействие с трёхзначной логикой.

Выбрали IS NULL и добавили тесты для трёх состояний: известная дата, NULL и значение, не удовлетворяющее другим условиям. В результате отчёт стал включать только действительно активные заказы, а поведение фильтра было зафиксировано тестами.

Что кандидаты часто упускают

  1. Что вернёт условие, если NULL участвует в выражении с OR?

    Если один операнд OR равен TRUE, весь результат равен TRUE независимо от второго операнда. Поэтому TRUE OR UNKNOWN даёт TRUE. Но FALSE OR UNKNOWN даёт UNKNOWN, и такая строка будет исключена WHERE.

    Следовательно, наличие OR не означает автоматического сохранения строк с NULL. Нужно вычислять итог по правилам трёхзначной логики, а не только проверять отдельные части условия.

  2. Почему отрицание сравнения с NULL не превращает UNKNOWN в TRUE?

    В SQL NOT UNKNOWN остаётся UNKNOWN. Поэтому отрицание условия, сравнивающего столбец с NULL, также не выберет строки с NULL.

    Для поиска строк, где значение отсутствует, нужен IS NULL. Для поиска всех известных значений нужен IS NOT NULL, а не отрицание обычного сравнения с NULL.

  3. Как сравнить два nullable-столбца с учётом того, что два NULL должны считаться равными?

    Обычное сравнение не подходит: NULL и NULL дадут UNKNOWN. Переносимый вариант — проверить равенство известных значений отдельно и добавить случай, когда оба столбца имеют NULL.

    В СУБД, поддерживающих IS NOT DISTINCT FROM, этот предикат выражает требуемую семантику напрямую: одинаковые известные значения считаются равными, и пара NULL также считается совпадением. Перед использованием нужно проверить поддержку конкретной СУБД.