Какое значение вернёт CASE, если ни одно условие не выполнено, а ветка ELSE отсутствует?
Если ни одно условие CASE не выполнено и ветка ELSE отсутствует, выражение возвращает NULL. Это не ошибка и не пустая строка: результат становится отсутствующим значением, поэтому дальнейшие сравнения и фильтрация обрабатывают его по правилам трёхзначной логики.
Условные выражения появились в SQL, чтобы выполнять выбор значения внутри запроса без процедурного кода. Они позволяют преобразовывать, классифицировать и вычислять данные непосредственно в SELECT, INSERT или UPDATE.
Отсутствие обязательной ветки ELSE сделано удобным для случаев, когда отсутствие подходящего условия должно естественно обозначаться значением NULL. Однако такая краткость опасна, если все возможные варианты должны быть обработаны явно.
Например, запрос может преобразовывать числовой код статуса в текстовое описание. Если в таблице появится новый код, которого нет среди условий, результатом станет NULL.
Это может привести к неожиданным итогам: строка не попадёт в фильтр с обычным сравнением, значение не будет отображаться в отчёте, а при записи в столбец с ограничением NOT NULL операция завершится ошибкой.
CASE последовательно проверяет условия. Возвращается результат первой подходящей ветки; если подходящей ветки нет, используется ELSE. Когда ELSE не указан, стандартное поведение эквивалентно возврату NULL.
Для значения amount, равного нулю или NULL, ни одно условие не даст результат TRUE, поэтому category будет NULL. В условии WHERE category = 'обычный' такая строка не будет выбрана: сравнение NULL с текстом даёт UNKNOWN, а WHERE оставляет только строки с результатом TRUE.
Если отсутствие категории недопустимо, следует явно задать ELSE, например ELSE 'неизвестный'. Это делает поведение при появлении новых или ошибочных значений заметным и предотвращает неявное распространение NULL.
Для простого CASE, сравнивающего одно выражение со значениями, действует тот же принцип: при отсутствии совпадения без ELSE возвращается NULL. При этом сравнение с NULL не считается совпадением; для проверки отсутствующего значения нужны предикаты IS NULL или IS NOT NULL.
Также все возвращаемые ветками значения должны быть совместимы по типу согласно правилам конкретной СУБД. Поэтому добавление ELSE должно учитывать не только смысл результата, но и его тип.
В отчёте код тарифа преобразовывали в название через CASE без ELSE. После добавления нового тарифа отчёт начал показывать пустые категории, а экспорт в систему аналитики стал отклоняться из-за обязательного текстового поля.
Рассматривались два варианта. Можно было оставить NULL и обрабатывать его в каждом потребителе, но это усложняло интеграции и скрывало неизвестные коды. Можно было использовать ELSE 'неизвестный тариф', что сохраняло строки и делало проблему видимой.
Выбрали явный ELSE и отдельную проверку неизвестных кодов. В результате отчёт перестал терять смысловые значения, а появление нового тарифа стало обнаруживаться мониторингом, а не по ошибкам downstream-систем.
NULL, возвращённый CASE, пустой строкой или нулём?Нет. NULL означает отсутствие известного значения и не равен ни пустой строке, ни числу ноль. Поэтому функции, сортировка, агрегаты и сравнения могут обрабатывать его иначе, чем обычное значение; например, COUNT(column) обычно не учитывает NULL, тогда как COUNT(*) учитывает строку.
Обычное сравнение с NULL не даёт TRUE: его результатом становится UNKNOWN. Поэтому ветка не сработает, даже если проверяемое выражение тоже равно NULL; для такого случая требуется отдельная проверка IS NULL.
Логически результат определяется выбранной веткой, и CASE обычно используют для условного вычисления. Но нельзя безоговорочно считать его универсальным барьером от любого предварительного вычисления: оптимизатор или планировщик может заранее обработать некоторые константные выражения, а конкретная СУБД имеет собственные правила оптимизации.
Поэтому CASE не следует использовать как единственную защиту от потенциально опасных выражений во всех обстоятельствах. Для критичных случаев нужно учитывать документацию конкретной СУБД и строить выражение так, чтобы недопустимая операция не возникала уже на этапе формирования плана или вычисления констант.