CASE WHEN — оператор условной логики SQL, закреплённый в стандарте ANSI SQL-92 и аналог конструкции if/else в процедурных языках — Python, Java, JavaScript. Он не изменяет данные в таблице, а возвращает одно из нескольких значений в зависимости от условий. Конструкция case when обязательно завершается END и работает в шести контекстах: SELECT, ORDER BY, GROUP BY, HAVING, WHERE и UPDATE. Поддерживается в PostgreSQL, MySQL, SQL Server, Oracle и SQLite без изменений. В статье — две синтаксических формы, примеры кода для каждого контекста и нюансы конкретных СУБД.
CASE WHEN — выражение, которое проверяет последовательность условий и возвращает значение первого сработавшего. Это не процедура и не функция: конструкция встраивается прямо в запрос и создаёт вычисляемое значение в результирующем наборе, не изменяя данные в таблице.
Учитесь бесплатно за счёт государства
Экономия до 100 000 ₽ на любой программе
По смыслу условный оператор CASE повторяет if/else из Python или Java и switch/case из JavaScript: перебирает условия сверху вниз, останавливается на первом совпадении, возвращает соответствующее значение. Если ни одна ветка не сработала и ELSE не задан — возвращает NULL.
Зачем это нужно? Вместо того чтобы тянуть сырые коды статусов на уровень приложения и переводить их там, логику категоризации, замены и фильтрации держат прямо в SQL. Аналитические отчёты, сегментация пользователей, разметка воронки — всё строится через условную логику в одном запросе без дополнительных JOIN или подзапросов.
У конструкции case when sql две синтаксических формы. Обе завершаются обязательным END — пропуск этого ключевого слова даёт ошибку синтаксиса (syntax error), и это самый частый промах при первом знакомстве с оператором. Правило расстановки веток: строгие, узкие условия — первыми, широкие и общие — ниже.
| Характеристика |
Форма searched CASE |
Форма simple CASE |
|---|---|---|
| Тип условий | Произвольные булевы выражения | Только точное равенство |
| Поддержка IS NULL | ✅ | ❌ |
| Поддержка диапазонов (≥, <) | ✅ | ❌ |
| AND/OR в одном WHEN | ✅ | ❌ |
| Применимость | ~90% случаев | Коды, статусы, фиксированные наборы |
Форма с произвольными условиями (searched CASE) — самая гибкая:
CASE
WHEN условие_1 THEN результат_1
WHEN условие_2 THEN результат_2
ELSE результат_по_умолчанию
END
В каждом WHEN можно писать диапазоны, объединять несколько колонок через AND/OR, проверять на NULL через IS NULL. Пример — сегментация пользователей по сумме покупок:
SELECT
user_id,
CASE
WHEN total_spent >= 50000 THEN ‘platinum’
WHEN total_spent >= 10000 THEN ‘gold’
WHEN total_spent >= 2000 THEN ‘silver’
ELSE ‘bronze’
END AS tier
FROM users;
Условия проверяются сверху вниз; срабатывает первое совпавшее. ELSE задаёт значение по умолчанию — без него строки, не попавшие ни в одну ветку, получат NULL.
Форма с фиксированным выражением (simple CASE) компактнее там, где нужно сравнить одну колонку с конкретными значениями:
CASE выражение
WHEN значение_1 THEN результат_1
WHEN значение_2 THEN результат_2
ELSE результат_по_умолчанию
END
Типичное применение — перевод кодов и статусов:
SELECT
order_id,
CASE status
WHEN ‘pending’ THEN ‘В ожидании’
WHEN ‘paid’ THEN ‘Оплачен’
WHEN ‘shipped’ THEN ‘Отправлен’
ELSE ‘Неизвестно’
END AS status_ru
FROM orders;
Ограничение: только точное равенство. IS NULL и диапазоны в этой форме не работают — для таких случаев используйте searched CASE.

Конструкция case when sql применима в шести контекстах: SELECT (создаёт вычисляемый столбец), ORDER BY (нестандартная сортировка), GROUP BY (группировка по вычисляемому признаку), HAVING (фильтрация групп), WHERE (ветвление по параметру) и UPDATE (массовое обновление по разным правилам).
Самый частый паттерн — CASE WHEN в SELECT создаёт вычисляемую колонку «на лету», не трогая данные в таблице:
SELECT
user_id,
total_spent,
CASE
WHEN total_spent >= 50000 THEN ‘platinum’
WHEN total_spent >= 10000 THEN ‘gold’
WHEN total_spent >= 2000 THEN ‘silver’
ELSE ‘bronze’
END AS tier
FROM users;
Колонка tier существует только в результате запроса. Подходит для дашбордов, отчётов и промежуточных вычислений без изменения схемы таблицы.
В ORDER BY оператор задаёт нестандартный бизнес-порядок сортировки:
ORDER BY
CASE status
WHEN ‘paid’ THEN 1
WHEN ‘pending’ THEN 2
ELSE 3
END;
В GROUP BY возникает нюанс: MySQL и PostgreSQL позволяют группировать по алиасу из SELECT (GROUP BY tier), а SQL Server — нет. Универсальное решение — общее табличное выражение (CTE, Common Table Expression): вычислить CASE в WITH-блоке, затем группировать по алиасу из него.
В WHERE конструкция нужна только при ветвлении логики по внешнему параметру — например, по роли пользователя. Простые фильтры лучше записывать через AND/OR: CASE в WHERE не позволяет базе задействовать индексы.
В UPDATE case when позволяет массово обновить строки по разным правилам за один проход:
UPDATE products
SET price = CASE
WHEN category = ‘premium’ THEN price * 1.10
WHEN category = ‘sale’ THEN price * 0.85
ELSE price
END;
Условные агрегаты — ключевой аналитический паттерн: несколько метрик с разными условиями в одном запросе без дополнительных подзапросов и JOIN. Вместо нескольких отдельных SELECT достаточно одного с несколькими CASE WHEN внутри агрегатных функций.
Разобраться в SQL-аналитике глубже поможет программа «Специалист по аналитике и базам данных в информационных системах» на 72 часа — обучение проходит онлайн и бесплатно в рамках нацпроекта «Кадры». Подробнее — в каталоге программ обучения.
Паттерн: SUM агрегирует только те строки, где условие выполнено, остальные дают 0 через ELSE:
SELECT
DATE_TRUNC(‘month’, created_at) AS month,
SUM(CASE WHEN channel = ‘organic’ THEN amount ELSE 0 END) AS organic_revenue,
SUM(CASE WHEN channel = ‘paid’ THEN amount ELSE 0 END) AS paid_revenue
FROM orders
GROUP BY 1;
Выручка по каналам за каждый месяц — в одном запросе. Технически SUM игнорирует NULL, поэтому ELSE можно опустить. Но ELSE 0 делает намерение явным: читатель запроса сразу видит, что несовпавшие строки дают ноль, а не пустоту. Это хорошая практика читаемости.
Здесь ELSE специально не указывают: COUNT не считает NULL, поэтому строки без совпадения автоматически пропускаются:
SELECT
COUNT(*) AS registrations,
Хотите сменить профессию или повысить квалификацию?
Федеральный проект «Активные меры содействия занятости» даёт возможность пройти обучение бесплатно за счёт государства
COUNT(CASE WHEN onboarded_at IS NOT NULL THEN 1 END) AS onboarded,
COUNT(CASE WHEN first_order_at IS NOT NULL THEN 1 END) AS buyers,
COUNT(CASE WHEN first_order_at IS NOT NULL THEN 1 END) * 100.0
/ COUNT(*) AS conversion_pct
FROM users;
За один запрос получаем всю воронку: регистрации → онбординг → покупатели → процент конверсии.
CASE можно вкладывать в THEN другого CASE. Один уровень вложенности читается нормально:
CASE
WHEN is_premium = 1 THEN
CASE WHEN total_spent >= 50000 THEN ‘Premium VIP’ ELSE ‘Premium’ END
ELSE ‘Free’
END AS segment
При двух уровнях и глубже код становится трудночитаемым и сложно поддерживаемым. Решение — общее табличное выражение (CTE): вычислить промежуточные значения в WITH-блоке, а в основном SELECT работать с алиасом без повторения всей конструкции:
WITH ranked AS (
SELECT
user_id,
CASE
WHEN total_spent >= 50000 THEN ‘platinum’
ELSE ‘bronze’
END AS tier
FROM users
)
SELECT tier, COUNT(*) FROM ranked GROUP BY tier;
Для сложных матриц условий альтернативный подход — справочная таблица с JOIN вместо глубокой вложенности.
Антипаттерн, который не даёт предупреждений при выполнении запроса:
— Неправильно: всегда возвращает False
WHEN email = NULL THEN ‘пусто’
— Правильно
WHEN email IS NULL THEN ‘пусто’
NULL = NULL в SQL никогда не даёт True. Единственный способ проверить NULL — IS NULL / IS NOT NULL. Это правило распространяется на все ветки CASE без исключений.
Вторая ловушка: case when null — отсутствие ELSE. Если ни одна ветка не совпала, оператор вернёт NULL. В агрегатных функциях COUNT и SUM это работает в пользу разработчика — NULL игнорируется. В SELECT для дашборда — нежелательные пустые ячейки.
Для простой NULL-замены используйте COALESCE(x, 0) — это краткая запись CASE WHEN x IS NOT NULL THEN x ELSE 0 END. При сложной условной логике с несколькими ветками CASE остаётся предпочтительным.
CASE WHEN входит в стандарт ANSI SQL и работает одинаково в любой реляционной базе данных. Различия — только в дополнительных возможностях: функции-сокращения, поддержка GROUP BY по алиасу и процедурный контекст.
| Возможность |
PostgreSQL |
MySQL |
MS SQL |
Oracle |
SQLite |
|---|---|---|---|---|---|
| CASE WHEN (ANSI) | ✅ | ✅ | ✅ | ✅ | ✅ |
| FILTER (условный агрегат) | ✅ | ❌ | ❌ | ❌ | ✅ (3.30+) |
| IF(cond, a, b) | ❌ | ✅ | ❌ | ❌ | ❌ |
| IIF(cond, a, b) | ❌ | ❌ | ✅ | ❌ | ❌ |
| GROUP BY по алиасу из SELECT | ✅ | ✅ | ❌ | ❌ | ✅ |
PostgreSQL полностью поддерживает стандартный синтаксис, GROUP BY по алиасу допустим. Уникальная особенность — оператор FILTER для условных агрегатов:
SELECT
COUNT(*) FILTER (WHERE tier = ‘free’) AS free_users,
COUNT(*) FILTER (WHERE tier = ‘premium’) AS premium_users
FROM users;
FILTER читается компактнее SUM+CASE для простых условий. При сложной многоветочной логике CASE WHEN предпочтительнее — он переносим на другие СУБД.
MySQL поддерживает CASE WHEN полностью, GROUP BY по алиасу допустим. Дополнительно доступна функция IF(условие, значение_если_истина, значение_если_ложь) для бинарного выбора:
SELECT IF(status = ‘paid’, ‘Оплачен’, ‘Не оплачен’) AS status_ru FROM orders;
IF() — не ANSI SQL, работает только в MySQL и ClickHouse. Для трёх и более условий используйте case when.
MS SQL Server поддерживает CASE WHEN стандартно. Дополнительно доступен IIF(условие, значение_a, значение_b) — синтаксический сахар для двух веток:
SELECT IIF(total_spent >= 10000, ‘VIP’, ‘Standard’) AS segment FROM customers;
Важный нюанс: GROUP BY по алиасу из SELECT в MS SQL не работает. Дублируйте выражение в GROUP BY или применяйте общее табличное выражение (CTE).
В SQL-запросах Oracle поддерживает стандартный ANSI CASE без изменений. В процедурном языке Oracle — PL/SQL (Procedural Language/SQL) — CASE выступает самостоятельным оператором внутри блока DECLARE/BEGIN/END и завершается END CASE:
DECLARE
v_tier VARCHAR2(20);
BEGIN
CASE
WHEN :total_spent >= 50000 THEN v_tier := ‘platinum’;
WHEN :total_spent >= 10000 THEN v_tier := ‘gold’;
ELSE v_tier := ‘bronze’;
END CASE;
DBMS_OUTPUT.PUT_LINE(v_tier);
END;
Это единственная СУБД, где CASE используется вне SQL-запроса в процедурном контексте.
SQLite поддерживает обе формы CASE WHEN в полном объёме. Применяется в мобильных приложениях, встраиваемых системах и прототипах, где нужна лёгкая база без серверной инфраструктуры.
Шесть антипаттернов, с которыми сталкиваются даже опытные разработчики:

| Конструкция |
Что делает |
Когда выбрать |
СУБД |
|---|---|---|---|
| CASE WHEN … END | Условная логика с N ветками | Основной инструмент | Все |
| COALESCE(x, default) | Первое не-NULL значение из списка | Простая NULL-замена | Все |
| NULLIF(a, b) | NULL если a равно b | Защита от деления на ноль | Все |
| IF(cond, a, b) | Бинарное условие, 2 ветки | Быстрая проверка | MySQL, ClickHouse |
| IIF(cond, a, b) | Бинарное условие, 2 ветки | Быстрая проверка | SQL Server |
| COUNT(*) FILTER (WHERE …) | Условный агрегат | Компактные агрегаты в PostgreSQL | PostgreSQL, SQLite 3.30+ |
COALESCE и NULLIF — узкоспециализированные инструменты для работы с NULL и делением. IF/IIF закрывают бинарную логику в конкретных СУБД. CASE WHEN — универсальный инструмент для любой условной логики с двумя и более ветками, переносимый между всеми основными базами данных без изменений.
Вернёт NULL. В агрегатных функциях COUNT и SUM это нормально — NULL игнорируется автоматически. В SELECT для дашборда или отчёта нежелательно: добавьте ELSE с запасным значением, чтобы избежать пустых ячеек.
Да, вложенность синтаксически допустима. Оптимальная глубина — один уровень. При двух уровнях и более вынесите промежуточные вычисления в общее табличное выражение (CTE) через WITH — это улучшит читаемость и упростит отладку.
IIF(cond, a, b) — сокращение для CASE WHEN cond THEN a ELSE b END. Поддерживает только две ветки и доступен исключительно в SQL Server. Для трёх и более условий используйте полный синтаксис case when.
Когда нужна только простая NULL-замена: COALESCE(x, 0) короче и читаемее. При сложной условной логике — несколько условий, разные значения, диапазоны — CASE WHEN остаётся предпочтительным.
В большинстве контекстов — нет. Исключение: CASE в предикате WHERE блокирует использование индексов. Заменяйте обычными условиями AND/OR; CASE в WHERE оправдан только при ветвлении логики по внешнему параметру — например, по роли пользователя.
Синтаксически — да. Но CASE в WHERE не позволяет базе данных задействовать индексы и усложняет план запроса. Используйте только при необходимости ветвить саму логику условия в зависимости от параметра, а не для обычной фильтрации.
GROUP BY вычисляется раньше SELECT — алиас из SELECT в этот момент ещё неизвестен ядру базы. PostgreSQL и MySQL допускают GROUP BY по алиасу напрямую; MS SQL и Oracle — нет. Универсальное решение: вычислить CASE в общем табличном выражении (CTE) и группировать по алиасу из него.
COUNT(*) FILTER (WHERE cond) компактнее и чище читается. Но FILTER доступен только в PostgreSQL и SQLite начиная с версии 3.30. SUM(CASE WHEN … ELSE 0 END) переносимо: работает в MySQL, MS SQL и Oracle. Выбирайте FILTER для PostgreSQL-проектов, CASE — для кросс-СУБД кода.
Подайте заявку —
забронируйте место в группе
45 000 мест на 2026 год. Бесплатное обучение по федеральному проекту «Активные меры содействия занятости»