CASE — это if/else в SQL
CASE — это способ положить условную логику внутрь запроса. Он по очереди проходит по веткам WHEN, выбирает первую совпавшую и возвращает значение после THEN. Если ничего не совпало, возвращается значение из ELSE — или NULL, если ELSE вы не написали.
Ключевое слово здесь — выражение. CASE возвращает значение, поэтому его можно поставить везде, где допустимо значение: столбец SELECT, ключ ORDER BY, правая часть сравнения, аргумент функции.
Вот и вся форма: CASE, одна или несколько WHEN ... THEN ..., опциональный ELSE, и потом END. END обязателен — забыть его — самая частая опечатка.
Реалистичный пример
Допустим, есть таблица заказов и хочется проставить каждой строке метку по размеру. Соберём небольшой набор данных прямо в запросе, чтобы можно было запустить:
Ветки проверяются сверху вниз. Побеждает первое совпадение, поэтому упорядочивайте их от самого узкого условия к самому общему. ELSE ловит всё, что не совпало с предыдущими ветками — без него 1200.00 вернулся бы как NULL вместо 'large'.
Поисковая форма против простой
Выше — поисковая форма: у каждой WHEN своё булево условие. Когда вы сравниваете одно выражение с несколькими константами, есть более короткая — простая — форма:
Выражение после CASE вычисляется один раз и сравнивается через = с каждым значением WHEN. Так чище, когда вы делаете лукапы по равенству на одном столбце.
Один нюанс: простой CASE использует =, а в SQL NULL = NULL — это не true. Если status может оказаться NULL, ветки 'A'/'B'/'C' его не поймают — и сработает ELSE. Чтобы обработать NULL явно, переключайтесь на поисковую форму и пишите WHEN status IS NULL THEN ....
CASE в ORDER BY
ORDER BY принимает любое выражение, и CASE там нормально работает. Полезно, когда нужен свой порядок сортировки, не совпадающий с алфавитным или числовым:
По алфавиту получается 'high' < 'low' < 'medium', что для триажа бесполезно. Сопоставление каждого приоритета числу через CASE даёт тот порядок, который вам реально нужен. Хвостовое , id стабильно ломает ничьи.
CASE в WHERE
CASE можно засунуть и в WHERE, но обычно в этом нет смысла — обычные цепочки AND/OR читаются понятнее. Где CASE правда блестит — это когда само условие зависит от другого значения:
Распродажные товары проходят при цене ниже 20, обычные — ниже 30. Сам порог условный. Без CASE пришлось бы писать (on_sale = 1 AND price < 20) OR (on_sale = 0 AND price < 30) — тот же результат, только шумнее.
CASE внутри агрегатов
Вот где CASE по-настоящему окупается. Скомбинируйте его с SUM или COUNT, и вы за один проход посчитаете итоги по подмножеству строк — SQL-эквивалент «посчитай, сколько из этих подходят»:
CASE возвращает 1 на совпавших строках и 0 на остальных, поэтому SUM превращается в условный счётчик. Тот же приём работает и для выручки — на нужных строках возвращайте total, а на остальных 0. Один проход по таблице — несколько условных агрегатов сразу.
IIF: короткая форма для двух веток
Для одного условия с двумя исходами в SQLite есть IIF(cond, when_true, when_false). Это чистая короткая запись для CASE WHEN cond THEN when_true ELSE when_false END:
Берите IIF, когда логика бинарная и так читается лучше в одну строку. К CASE переходите, как только появляется три и более ветки, нужно отдельно обработать NULL или хочется ясный порядок проваливания через несколько WHEN.
Грабли, которые стоит знать
Вещи, на которые регулярно наступают:
- Забыли
END.CASEоткрывает блок;ENDего закрывает. SQLite даст ошибку парсинга где-то заметно позже самой ошибки. - Без
ELSEбудетNULL. Если ни однаWHENне совпала, аELSEвы пропустили, результат —NULL. Иногда это и нужно, но обычно — нет. - Порядок веток важен. В поисковой форме побеждает первая совпавшая
WHEN. ПоставитеWHEN total < 500раньшеWHEN total < 100— и до второй ветки выполнение никогда не дойдёт. - Смесь типов. Каждая ветка может вернуть свой тип, и SQLite на это не пожалуется — но дальше по конвейеру такой код легко ломает другую логику. Старайтесь, чтобы ветки возвращали совместимые типы (все текст, все числа).
- Простой
CASEиNULL. Как уже говорили: простая форма использует=, который никогда не совпадает сNULL. ЕслиNULL-ы в игре — переходите на поисковую форму.
Дальше: строковые функции
CASE помогает ветвиться по значениям; следующая глава — про преобразование значений. Строковые функции — UPPER, LOWER, SUBSTR, REPLACE, шаблоны LIKE — закрывают повседневную работу с очисткой и переформатированием текстовых столбцов. С них и начнём.
Часто задаваемые вопросы
Что такое выражение CASE в SQLite?
Выражение CASE — это SQL-эквивалент if/else: проверяет условия и возвращает значение. Это именно выражение, а не оператор, поэтому ставить его можно везде, где допустимо значение: в SELECT, WHERE, ORDER BY, UPDATE, и даже внутри агрегатов. Каждая ветка выглядит как WHEN условие THEN значение, плюс необязательный ELSE в конце.
В чём разница между простым и поисковым CASE в SQLite?
Простой CASE сравнивает одно выражение с несколькими значениями: CASE status WHEN 'A' THEN ... WHEN 'B' THEN ... END. Поисковый CASE в каждой ветке считает свой булев предикат: CASE WHEN price > 100 THEN ... WHEN qty = 0 THEN ... END. Поисковая форма гибче — в ней свободно смешиваются столбцы, операторы и проверки на NULL.
Когда в SQLite брать IIF вместо CASE?
IIF(cond, a, b) — это короткая запись для CASE WHEN cond THEN a ELSE b END. Берите IIF, когда логика двухветочная и так читается чище. К CASE имеет смысл переходить, как только веток становится три и больше, нужен порядок проваливания или явная обработка NULL через WHEN col IS NULL.