CASE: логика прямо в SELECT
Чему научишься
- размечать строки прямо в
SELECTчерезCASE WHEN ... THEN ... ELSE ... END - превращать сырые значения из таблицы в понятные метки для отчёта
- понимать, что условия в
CASEпроверяются сверху вниз - объяснять, что вернёт
CASE, если ни одинWHENне сработал - отличать поисковую форму
CASEот простой формыCASE - понимать, почему
WHEN NULLв простой форме не срабатывает никогда - использовать
CASEвORDER BY, чтобы сортировать строки по бизнес-порядку, а не по алфавиту
Если — то
Настоящий архивист не только читает данные — он размечает их.
В старом хранилище «Котомаркета» лежат сырые факты:
- цена товара;
- статус заказа;
- количество на складе;
- город пользователя;
- дата события.
Но человеку часто нужен не просто факт, а понятная метка.
Например, в таблице products есть цена:
| name | price |
|---|---|
| Игрушка «Мышь-перехватчик» | 900 |
| Книга «SQL для котонавтов» | 2500 |
| Домик-портал | 7200 |
Сама цена полезна, но в отчёте иногда хочется сразу видеть сегмент:
| name | price | segment |
|---|---|---|
| Игрушка «Мышь-перехватчик» | 900 | дешёвый |
| Книга «SQL для котонавтов» | 2500 | средний |
| Домик-портал | 7200 | дорогой |
Такую разметку делает CASE.
CASE — это выражение с ветвлением:
CASE
WHEN условие1 THEN значение1
WHEN условие2 THEN значение2
ELSE значение_по_умолчанию
END
Читается почти как обычная инструкция:
если условие 1 верно — верни значение 1;
иначе если условие 2 верно — верни значение 2;
иначе верни значение по умолчанию.
Пример:
SELECT
name,
price,
CASE
WHEN price < 1500 THEN 'дешёвый'
WHEN price < 4000 THEN 'средний'
ELSE 'дорогой'
END AS segment
FROM products;
Здесь CASE создаёт новый вычисляемый столбец segment.
Он не меняет данные в таблице.
Он только добавляет разметку в результат запроса.
КВЕРИ: Разметка — это мнение архивиста, записанное поверх фактов. Вешай ярлыки так, чтобы за них не было стыдно через сто лет.

CASE — ярлыки архивиста: каждая строка получает метку по первому сработавшему условию.CASE проверяет ветки сверху вниз: строка получает значение первого сработавшего WHEN, остальное достаётся ELSE.price — и каталог читается как готовый отчёт.CASE проверяет ветки сверху вниз
Самая важная деталь CASE:
срабатывает первый подходящий WHEN.
Как только SQL нашёл ветку, условие которой вернуло TRUE, он берёт значение после THEN и дальше ветки уже не проверяет.
Посмотрим на пример:
CASE
WHEN price < 4000 THEN 'не дорогой'
WHEN price < 1500 THEN 'дешёвый'
ELSE 'дорогой'
END
На первый взгляд кажется, что товар за 900 должен получить метку 'дешёвый'.
Но он её не получит.
Почему?
Для цены 900 первое условие уже верно:
price < 4000
Значит, CASE сразу вернёт:
не дорогой
и до ветки:
WHEN price < 1500 THEN 'дешёвый'
уже не дойдёт.
Поэтому порядок веток — это не оформление. Это часть логики.
Для диапазонов обычно идут от более узкого условия к более широкому:
CASE
WHEN price < 1500 THEN 'дешёвый'
WHEN price < 4000 THEN 'средний'
ELSE 'дорогой'
END
Так товар за 900 попадёт в первую ветку, а товар за 2500 — во вторую.
Что делает ELSE
ELSE — это запасной вариант.
Он срабатывает, если ни один WHEN не подошёл.
Например:
CASE
WHEN price < 1500 THEN 'дешёвый'
WHEN price < 4000 THEN 'средний'
ELSE 'дорогой'
END
Если цена 7200, первые два условия не подходят:
7200 < 1500 -- нет
7200 < 4000 -- нет
Значит, вернётся значение из ELSE:
дорогой
А что будет, если ELSE не написать?
CASE
WHEN price < 1500 THEN 'дешёвый'
WHEN price < 4000 THEN 'средний'
END
Если цена 7200, ни один WHEN не сработает. Запасного варианта нет.
В такой ситуации CASE вернёт NULL.
То есть:
если ни одна ветка не подошла и
ELSEотсутствует, результатом будетNULL.
Для отчётов часто лучше писать явный ELSE, чтобы результат был понятнее человеку:
ELSE 'без сегмента'
или:
ELSE 'дорогой'
Выбор зависит от бизнес-логики.
CASE — это выражение, а не отдельная команда
CASE возвращает значение.
Это значит, что его можно использовать там, где SQL ждёт значение:
- в
SELECT; - в
ORDER BY; - иногда в
WHERE; - внутри вычислений;
- внутри в следующих модулях.
В этом уроке главный сценарий — SELECT.
SELECT
name,
price,
CASE
WHEN price < 1500 THEN 'дешёвый'
WHEN price < 4000 THEN 'средний'
ELSE 'дорогой'
END AS segment
FROM products;
Здесь CASE работает как вычисляемый столбец.
Можно думать о нём так:
для каждой строки посчитай новое значение по условиям
У каждой строки получится свой результат.
У товара за 900 — 'дешёвый'.
У товара за 2500 — 'средний'.
У товара за 7200 — 'дорогой'.
CASE не фильтрует строки сам по себе. Он не похож на WHERE.
WHERE решает:
оставить строку или убрать?
CASE решает:
какое значение показать для этой строки?
CASE для бизнесовых меток
CASE часто используют, чтобы превратить технические значения в понятные человеку подписи.
Например, в таблице orders есть статусы:
| id | status |
|---|---|
| 1 | paid |
| 2 | pending |
| 3 | cancelled |
| 4 | refunded |
Для базы такие значения удобны: короткие, стабильные, на английском.
Но в отчёте можно показать русские метки:
SELECT
id,
status,
CASE
WHEN status = 'paid' THEN 'оплачен'
WHEN status = 'pending' THEN 'ожидает оплаты'
WHEN status = 'cancelled' THEN 'отменён'
WHEN status = 'refunded' THEN 'возврат'
ELSE 'неизвестный статус'
END AS status_label
FROM orders;
Результат:
| id | status | status_label |
|---|---|---|
| 1 | paid | оплачен |
| 2 | pending | ожидает оплаты |
| 3 | cancelled | отменён |
| 4 | refunded | возврат |
Такой запрос не меняет status в таблице.
Он просто добавляет рядом понятную расшифровку.
Это удобно для отчётов, выгрузок, и учебных задач, где результат должен говорить языком человека.
Поисковая форма CASE
Форма, которую мы использовали выше, называется поисковой:
CASE
WHEN условие1 THEN значение1
WHEN условие2 THEN значение2
ELSE значение_по_умолчанию
END
Её называют searched CASE.
В каждой ветке после WHEN можно писать полноценное условие:
WHEN price < 1500 THEN 'дешёвый'
WHEN status = 'paid' THEN 'оплачен'
WHEN stock = 0 THEN 'нет в наличии'
WHEN price < 1500 AND stock > 0 THEN 'дешёвый и есть в наличии'
Поисковая форма гибкая. В ней можно:
- сравнивать разные столбцы;
- использовать
ANDиOR; - проверять
NULLчерезIS NULL; - строить диапазоны;
- описывать сложные бизнес-правила.
Например:
SELECT
name,
price,
stock,
CASE
WHEN stock = 0 THEN 'нет в наличии'
WHEN price < 1500 AND stock > 0 THEN 'дешёвый товар в наличии'
WHEN price >= 1500 AND stock > 0 THEN 'товар в наличии'
ELSE 'проверь данные'
END AS product_note
FROM products;
Здесь каждая ветка — отдельное условие.
Именно поисковая форма чаще всего удобнее для новичков, потому что она прямо показывает логику.
Простая форма CASE
У CASE есть вторая форма — простая.
Она удобна, когда мы сравниваем один и тот же столбец с несколькими точными значениями.
Например, вместо такого запроса:
CASE
WHEN status = 'paid' THEN 'оплачен'
WHEN status = 'pending' THEN 'ожидает'
WHEN status = 'cancelled' THEN 'отменён'
WHEN status = 'refunded' THEN 'возврат'
ELSE 'другой статус'
END
можно написать короче:
CASE status
WHEN 'paid' THEN 'оплачен'
WHEN 'pending' THEN 'ожидает'
WHEN 'cancelled' THEN 'отменён'
WHEN 'refunded' THEN 'возврат'
ELSE 'другой статус'
END
Здесь выражение status написано один раз:
CASE status
А дальше перечислены значения, с которыми его нужно сравнить:
WHEN 'paid' THEN 'оплачен'
WHEN 'pending' THEN 'ожидает'
Простая форма читается как таблица соответствий:
| status | status_label |
|---|---|
| paid | оплачен |
| pending | ожидает |
| cancelled | отменён |
| refunded | возврат |
Она хорошо подходит для простого маппинга:
если значение равно этому — покажи такую подпись.
Простая и поисковая форма: в чём разница
Сравним две формы рядом.
Поисковая форма:
CASE
WHEN status = 'paid' THEN 'оплачен'
WHEN status = 'pending' THEN 'ожидает'
ELSE 'другой статус'
END
Простая форма:
CASE status
WHEN 'paid' THEN 'оплачен'
WHEN 'pending' THEN 'ожидает'
ELSE 'другой статус'
END
Результат может быть одинаковым.
Но логика записи разная.
В простой форме SQL сам сравнивает выражение после CASE со значениями после WHEN:
status = 'paid'
status = 'pending'
В поисковой форме ты сам пишешь условия полностью:
WHEN status = 'paid'
WHEN status = 'pending'
Простая форма короче, когда условия однотипные.
Поисковая форма гибче, когда условия разные.
Например, такую логику простой формой нормально не выразить:
CASE
WHEN status IS NULL THEN 'статус не указан'
WHEN status = 'paid' AND paid_at IS NOT NULL THEN 'оплачен'
WHEN status = 'pending' THEN 'ожидает'
ELSE 'проверь заказ'
END
Здесь есть проверка NULL, проверка другого столбца paid_at и составное условие через AND.
Для таких случаев выбирают поисковую форму.
Практическое правило:
Если нужно сопоставить один столбец с набором точных значений — можно использовать простой CASE.
Если нужны условия, диапазоны, NULL, несколько столбцов или AND/OR — используй поисковый CASE.
Почему WHEN NULL в простом CASE не работает
У простой формы есть важный подводный камень.
Допустим, в orders.status иногда бывает NULL.
Новичок может написать так:
CASE status
WHEN 'paid' THEN 'оплачен'
WHEN NULL THEN 'статус не указан'
ELSE 'другой статус'
END
Кажется, что ветка:
WHEN NULL THEN 'статус не указан'
должна сработать, если status равен NULL.
Но она не сработает никогда.
Почему?
Простая форма CASE status WHEN ... сравнивает status со значениями веток через обычное равенство =.
То есть ветка WHEN NULL по смыслу превращается в:
status = NULL
А из урока про NULL ты уже знаешь:
status = NULL
не даёт TRUE.
Оно даёт UNKNOWN.
А ветка WHEN срабатывает только когда условие даёт TRUE.
Поэтому для проверки NULL нужна поисковая форма:
CASE
WHEN status IS NULL THEN 'статус не указан'
WHEN status = 'paid' THEN 'оплачен'
WHEN status = 'pending' THEN 'ожидает'
ELSE 'другой статус'
END
Здесь проверка написана правильно:
status IS NULL
Главное правило:
NULLвCASEпроверяют черезIS NULL, а значит — поисковой формой.
CASE превращает технические статусы заказов в понятные подписи.Значения THEN и ELSE должны быть совместимы
CASE возвращает одно значение.
Но разные ветки могут возвращать разные варианты этого значения.
Например:
CASE
WHEN price < 1500 THEN 'дешёвый'
WHEN price < 4000 THEN 'средний'
ELSE 'дорогой'
END
Все ветки возвращают текст. Это нормально.
'дешёвый'
'средний'
'дорогой'
А вот такой CASE выглядит подозрительно:
CASE
WHEN price < 1500 THEN 'дешёвый'
ELSE 0
END
В одной ветке возвращается текст:
'дешёвый'
В другой — число:
0
Базе нужно решить, какой тип будет у результата CASE: текстовый или числовой. В разных ситуациях это может привести к ошибке или неочевидному приведению типов.
Поэтому хорошая привычка:
ветки
THENиELSEдолжны возвращать значения одного понятного типа.
Если делаешь текстовую метку — возвращай текст во всех ветках:
CASE
WHEN price < 1500 THEN 'дешёвый'
ELSE 'не дешёвый'
END
Если делаешь числовой ранг — возвращай числа во всех ветках:
CASE
WHEN status = 'paid' THEN 1
WHEN status = 'pending' THEN 2
ELSE 3
END
Это особенно важно в ORDER BY, где CASE часто возвращает числовой приоритет.
CASE в ORDER BY: сортировка по бизнес-порядку
Иногда обычная сортировка не подходит.
Например, есть статусы заказов:
paid
pending
cancelled
refunded
Если отсортировать их по алфавиту:
SELECT id, status
FROM orders
ORDER BY status;
получится технический порядок по тексту.
Но бизнесу может быть нужен другой порядок:
- сначала оплаченные;
- потом ожидающие;
- потом отменённые;
- потом возвраты;
- потом всё остальное.
Алфавит не знает этого смысла. Зато его можно задать через CASE:
SELECT id, status
FROM orders
ORDER BY
CASE status
WHEN 'paid' THEN 1
WHEN 'pending' THEN 2
WHEN 'cancelled' THEN 3
WHEN 'refunded' THEN 4
ELSE 5
END,
id;
Здесь CASE возвращает числовой приоритет:
| status | priority |
|---|---|
| paid | 1 |
| pending | 2 |
| cancelled | 3 |
| refunded | 4 |
| другое | 5 |
ORDER BY сортирует по этому приоритету.
А id в конце нужен для стабильного порядка внутри одинаковых статусов:
ORDER BY CASE ... END, id
Так строки с одинаковым статусом будут отсортированы по id, а не в неопределённом порядке.
Можно показать метку и сортировать по той же логике
Иногда удобно одновременно:
- показать человеку понятную метку;
- отсортировать строки по бизнес-порядку.
Например:
SELECT
id,
status,
CASE status
WHEN 'paid' THEN 'оплачен'
WHEN 'pending' THEN 'ожидает'
WHEN 'cancelled' THEN 'отменён'
WHEN 'refunded' THEN 'возврат'
ELSE 'другой статус'
END AS status_label
FROM orders
ORDER BY
CASE status
WHEN 'paid' THEN 1
WHEN 'pending' THEN 2
WHEN 'cancelled' THEN 3
WHEN 'refunded' THEN 4
ELSE 5
END,
id;
В SELECT CASE создаёт текстовую метку:
status_label
В ORDER BY другой CASE создаёт числовой приоритет сортировки.
Почему не сортировать по самой русской метке?
Потому что алфавитный порядок меток снова может не совпасть с бизнес-логикой.
Например, по алфавиту 'возврат' может оказаться раньше 'оплачен', но бизнесу нужно наоборот.
Поэтому для отображения используют текст, а для порядка — числовой ранг.
Главное правило CASE
CASE проверяет ветки сверху вниз и возвращает результат первого WHEN, который дал TRUE.
CASE
WHEN price < 1500 THEN 'дешёвый'
WHEN price < 4000 THEN 'средний'
ELSE 'дорогой'
END
Порядок условий важен.
Если ни один WHEN не сработал и ELSE не написан, результатом будет NULL.
Для проверки NULL используй поисковую форму:
CASE
WHEN status IS NULL THEN 'статус не указан'
ELSE 'статус есть'
END
Не рассчитывай на WHEN NULL в простой форме.
Вопрос с собеседования
Вопрос с собеседования:
Что делает CASE в SQL?
Сильный ответ:
CASE — это выражение с ветвлением. Оно проверяет условия и возвращает значение для первой подходящей ветки. Его часто используют в SELECT, чтобы добавить в выдачу понятные метки, например сегмент цены или расшифровку статуса. CASE не меняет данные в таблице, а вычисляет значение в результате запроса.
Вопрос с собеседования:
В каком порядке проверяются ветки WHEN?
Сильный ответ:
Ветки проверяются сверху вниз. Срабатывает первый WHEN, условие которого вернуло TRUE. После этого остальные ветки уже не проверяются. Поэтому порядок условий в CASE — часть логики, а не просто оформление.
Вопрос с собеседования:
Что вернёт CASE, если ни один WHEN не сработал и ELSE не написан?
Сильный ответ:
Он вернёт NULL. Если для отчёта нужен понятный запасной вариант, лучше явно добавить ELSE, например ELSE 'другой статус' или ELSE 'без сегмента'.
Вопрос с собеседования:
Чем простой CASE отличается от поискового?
Сильный ответ:
Простой CASE записывается как CASE x WHEN a THEN ... WHEN b THEN ... END и сравнивает одно выражение x со значениями веток через =. Поисковый CASE записывается как CASE WHEN условие THEN ... END, и в каждой ветке можно писать полноценное условие: сравнения, диапазоны, AND/OR, IS NULL и проверки разных столбцов.
Вопрос с собеседования:
Почему ветка WHEN NULL в простом CASE никогда не срабатывает?
Сильный ответ:
Потому что простой CASE сравнивает выражение со значениями веток через =. Ветка WHEN NULL по смыслу превращается в x = NULL, а такое сравнение даёт UNKNOWN, а не TRUE. Ветка WHEN срабатывает только при TRUE. Поэтому NULL проверяют в поисковой форме: WHEN x IS NULL THEN ....
Вопрос с собеседования:
Зачем ставить CASE в ORDER BY?
Сильный ответ:
CASE в ORDER BY используют, когда нужен не алфавитный или числовой порядок, а бизнес-порядок. Например, статусы можно отсортировать так: сначала paid, потом pending, затем cancelled, затем refunded. Для этого каждому статусу через CASE назначают числовой приоритет и сортируют по нему.
CASE, если ни один WHEN не сработал и ELSE не написан?CASE?
CASE
WHEN price < 4000 THEN 'не дорогой'
WHEN price < 1500 THEN 'дешёвый'
ELSE 'дорогой'
END
Если price = 900.NULL?CASE, чтобы отсортировать статусы в бизнес-порядке?КВЕРИ:
CASE— это не просто «если — то». Это способ объяснить архиву, как человек должен читать сырые факты.