Оператор CASE в SQL: синтаксис и 12 примеров с выводом

CASE в SQL возвращает значение первой ветки WHEN, условие которой истинно, а без ELSE отдаёт NULL.

Разбираем простую и поисковую форму, CASE в SELECT, ORDER BY, GROUP BY, UPDATE и внутри COUNT и SUM.

Все 12 запросов прогнаны в SQLite, вывод приложен к каждому.

Статью написал:
Ваня Буявец, продюсер, основатель Checkroi
Ваня Буявец
Основатель Checkroi, продюсер, эксперт в выборе онлайн-курсов
Все 3090 статей автора Подписаться на Телеграм-канал
Одобрено экспертом:
Наташа Буявец, основатель Checkroi, эксперт по онлайн-курсам
Наташа Буявец
Основательница Checkroi, продюсер Youtube-каналов, эксперт по онлайн-курсам
Все 3744 экспертных мнения Подписаться на Телеграм-канал
Обложка: Оператор CASE в SQL: синтаксис и 12 примеров с выводом

CASE в SQL работает как if/else внутри запроса: проверяет условия по порядку и возвращает значение первой подходящей ветки. Общая запись такая: CASE WHEN условие THEN результат … ELSE результат END. Если ни одно условие не выполнилось, а ELSE нет, вернётся NULL.

CASE пишут в SELECT, ORDER BY, GROUP BY, внутри SUM и COUNT, в UPDATE и WHERE. Ниже синтаксис обеих форм, примеры с реальным выводом и ошибки, на которых CASE молча отдаёт не тот результат. Если вы только начинаете, сначала прочитайте, что такое язык SQL и как устроен запрос.

CheckroiCheckroiПодборка курсов по SQL422 курса • 40 школСравните цены, школы, программу и найдите выгодные предложения по обучениюСравнить→

Синтаксис CASE: две формы

Рой раскладывает карточки заказов по трём лоткам

У выражения две записи. Поисковая проверяет любые логические условия, простая сравнивает одно выражение со списком значений.

-- поисковый CASE
CASE
    WHEN условие_1 THEN результат_1
    WHEN условие_2 THEN результат_2
    ELSE результат_по_умолчанию
END

-- простой CASE
CASE выражение
    WHEN значение_1 THEN результат_1
    WHEN значение_2 THEN результат_2
    ELSE результат_по_умолчанию
END
Форма Что проверяет Когда брать
Поисковый CASE любое условие: <, >, BETWEEN, IS NULL, LIKE диапазоны, NULL, условия по нескольким столбцам
Простой CASE равенство одного выражения значениям из WHEN перевод кодов в названия: статусы, типы, флаги

Простая форма, по описанию CASE в документации PostgreSQL, «больше похожа на оператор switch», знакомый по C. Если писали на нём, вот разбор switch case и других управляющих конструкций C.

Тестовая таблица

Все запросы ниже я прогнал в SQLite 3.51 на одной таблице заказов. Вывод скопирован из консоли, ничего не дописано руками. Синтаксис CASE одинаков в PostgreSQL, MySQL, SQL Server и Oracle, различия я отмечу там, где они есть.

CREATE TABLE orders (id INTEGER PRIMARY KEY, city TEXT, amount INTEGER, status TEXT);
INSERT INTO orders VALUES
 (1, 'Москва', 1200, 'paid'),
 (2, 'Казань', 450, 'paid'),
 (3, 'Москва', 8900, 'new'),
 (4, 'Пермь', NULL, 'cancelled'),
 (5, 'Казань', 3100, 'paid'),
 (6, 'Москва', 15000, 'paid');

Поисковый CASE: разбить заказы по сумме

SELECT id, amount,
       CASE
           WHEN amount < 1000 THEN 'мелкий'
           WHEN amount < 5000 THEN 'средний'
           WHEN amount >= 5000 THEN 'крупный'
           ELSE 'без суммы'
       END AS size
FROM orders;
id  amount  size
--  ------  ---------
1   1200    средний
2   450     мелкий
3   8900    крупный
4           без суммы
5   3100    средний
6   15000   крупный

У заказа 4 сумма NULL. Сравнение с NULL не даёт истины, поэтому ни одна ветка WHEN не сработала и запрос ушёл в ELSE.

Простой CASE: перевести статусы

SELECT id,
       CASE status
           WHEN 'paid' THEN 'оплачен'
           WHEN 'new'  THEN 'новый'
           ELSE 'отменён'
       END AS status_ru
FROM orders;
id  status_ru
--  ---------
1   оплачен
2   оплачен
3   новый
4   отменён
5   оплачен
6   оплачен

Как CASE выбирает ветку

Правил три, и на каждом новички хотя бы раз получают странный результат.

  1. Побеждает первая истинная ветка. Microsoft пишет, что CASE «оценивает условия последовательно и останавливается с первым условием, условие которого удовлетворено» (справка по Transact-SQL). Остальные WHEN уже не проверяются.
  2. Без ELSE получится NULL. Если ни одно условие не выполнилось и ELSE нет, «результатом выражения будет NULL», так сказано в разделе об условных выражениях PostgreSQL.
  3. Все ветки возвращают один тип. Документация PostgreSQL требует, чтобы результаты приводились «к одному выходному типу», а SQL Server требует, чтобы они были «одинаковыми или неявно приводимыми друг к другу».

Вот что делает CASE без ELSE: статусы new и cancelled остались пустыми.

SELECT id, status,
       CASE status WHEN 'paid' THEN 'оплачен' END AS status_ru
FROM orders;
id  status     status_ru
--  ---------  ---------
1   paid       оплачен
2   paid       оплачен
3   new
4   cancelled
5   paid       оплачен
6   paid       оплачен

Пустая ячейка здесь означает NULL. Подробнее о том, чем NULL отличается от пустой строки и нуля, я писал в разборе NULL в SQL.

Канал основателя Checkroi Вани Буявца3 700 человек читают мой Телеграм-канал про нейросетиСобрал промпты для Claude Code и ChatGPT, разборы ИИ-инструментов и лайфхаки по продвижению бизнеса в одном местеПрисоединиться

Где писать CASE в запросе

Строки таблицы на развилке расходятся по разным дорожкам

CASE можно использовать везде, где допустимо выражение: в SELECT, UPDATE, DELETE, SET, WHERE, ORDER BY и HAVING. Это прямо перечислено в справке Microsoft. Если с порядком частей запроса пока путаница, загляните в разбор команды SELECT от WHERE до JOIN.

CASE внутри COUNT и SUM

Самый частый рабочий приём аналитика: посчитать по группам сразу несколько показателей. Сколько всего заказов в городе, сколько оплачено и на какую сумму:

SELECT city,
       COUNT(*) AS orders_total,
       COUNT(CASE WHEN status = 'paid' THEN 1 END) AS paid_cnt,
       SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS paid_sum
FROM orders
GROUP BY city
ORDER BY paid_sum DESC;
city    orders_total  paid_cnt  paid_sum
------  ------------  --------  --------
Москва  3             2         16200
Казань  2             2         3550
Пермь   1             0         0

В COUNT ветку ELSE специально не пишем: неподходящие строки дают NULL, а COUNT пропускает NULL. В SUM, наоборот, стоит ELSE 0, иначе у Перми в сумме будет NULL, а не ноль. Больше вариантов подсчёта я собрал в статье про COUNT с условием и по группам.

В PostgreSQL то же самое короче пишется через FILTER: COUNT(*) FILTER (WHERE status = 'paid'). По описанию агрегатных выражений, с FILTER «агрегатной функции подаются только те входные строки», для которых условие истинно. В MySQL и SQL Server FILTER нет, там остаётся CASE.

CASE в ORDER BY: свой порядок сортировки

Нужно показать сначала новые заказы, потом оплаченные, а отменённые в конце. По алфавиту так не отсортировать, а через CASE можно:

SELECT id, status
FROM orders
ORDER BY CASE status
             WHEN 'new'  THEN 1
             WHEN 'paid' THEN 2
             ELSE 3
         END, id;
id  status
--  ---------
3   new
1   paid
2   paid
5   paid
6   paid
4   cancelled

CASE в GROUP BY

SELECT CASE
           WHEN amount < 5000 THEN 'до 5000'
           ELSE '5000 и больше'
       END AS bucket,
       COUNT(*) AS cnt
FROM orders
WHERE amount IS NOT NULL
GROUP BY CASE
             WHEN amount < 5000 THEN 'до 5000'
             ELSE '5000 и больше'
         END;
bucket         cnt
-------------  ---
5000 и больше  2
до 5000        3

Почему выражение написано дважды? В SQL Server выражение GROUP BY «не может содержать псевдоним столбца», заданный в SELECT (справка по GROUP BY). PostgreSQL разрешает ссылаться в GROUP BY на имя выходного столбца (документация SELECT), SQLite тоже принял короткое GROUP BY bucket с тем же выводом. Полная запись работает везде.

CASE в UPDATE

Одним запросом можно поменять разные строки по-разному. Например, дать скидку 10 % на оплаченные заказы из Казани, а остальные не трогать:

UPDATE orders
SET amount = CASE
                 WHEN city = 'Казань' THEN amount * 0.9
                 ELSE amount
             END
WHERE status = 'paid';

SELECT id, city, amount FROM orders WHERE status = 'paid';
id  city    amount
--  ------  ------
1   Москва  1200
2   Казань  405
5   Казань  2790
6   Москва  15000

Ветка ELSE amount обязательна. Без неё московские заказы получили бы NULL вместо суммы.

Перед UPDATE на рабочей базе я сначала запускаю тот же CASE внутри SELECT и смотрю, какие значения получатся. Удобнее всего делать это в графическом клиенте, вот как подключить DBeaver к PostgreSQL.

CASE в WHERE

Технически работает: в Москве ищем заказы дороже 5000, в остальных городах дороже 1000.

SELECT id, city, amount
FROM orders
WHERE CASE
          WHEN city = 'Москва' THEN amount > 5000
          ELSE amount > 1000
      END;

Но то же условие читается проще через AND и OR, и вывод у обоих запросов совпал:

SELECT id, city, amount
FROM orders
WHERE (city = 'Москва' AND amount > 5000)
   OR (city <> 'Москва' AND amount > 1000);
id  city    amount
--  ------  ------
3   Москва  8900
5   Казань  3100
6   Москва  15000

Я бы оставлял CASE в WHERE только там, где через AND и OR условие становится нечитаемым: такой фильтр труднее проверить глазами и легко сломать при правке.

Вложенный CASE

Внутри ветки THEN или ELSE можно написать ещё один CASE. Так удобно делить крупные заказы по городу, а остальные не трогать:

SELECT id, amount,
       CASE
           WHEN amount IS NULL THEN 'нет данных'
           WHEN amount >= 5000 THEN
               CASE WHEN city = 'Москва' THEN 'крупный, Москва' ELSE 'крупный, регион' END
           ELSE 'обычный'
       END AS segment
FROM orders;
id  amount  segment
--  ------  ---------------
1   1200    обычный
2   450     обычный
3   8900    крупный, Москва
4           нет данных
5   3100    обычный
6   15000   крупный, Москва

В SQL Server глубина ограничена: справка Microsoft допускает «только 10 уровней вложения» в CASE. На практике три уровня уже читаются тяжело. Если вложенность растёт, вынесите классификацию в отдельный столбец или справочную таблицу и соедините через JOIN. Как пишется JOIN, показано в разборе SELECT с примерами.

CASE, IF, IIF и COALESCE: что выбрать

CASE есть во всех популярных СУБД: PostgreSQL, MySQL, SQL Server, Oracle, SQLite. Короткие функции удобнее, но часть из них привязывает запрос к одной базе.

Задача Стандартный вариант Короткий вариант
Одно условие, два исхода CASE WHEN … THEN … ELSE … END IF() в MySQL, IIF() в SQL Server
Подставить значение вместо NULL CASE WHEN x IS NULL THEN 0 ELSE x END COALESCE(x, 0)
Превратить значение в NULL CASE WHEN x = 0 THEN NULL ELSE x END NULLIF(x, 0)
Условный подсчёт COUNT(CASE WHEN … THEN 1 END) COUNT(*) FILTER (WHERE …) в PostgreSQL

По справке Microsoft, IIF «является быстрым способом написания выражения CASE» и переводится в CASE, поэтому тоже вкладывается «максимум до уровня 10». COALESCE и NULLIF описаны в той же главе документации PostgreSQL, что и CASE. Для MySQL функции IF, IFNULL и NULLIF собраны в разделе Flow Control Functions.

Частые ошибки с CASE и как их найти

Рой довольно ставит галочку на распечатке отчёта

Простой CASE не видит NULL

Запрос выглядит логично, но строка с NULL в сумме не попадает в нужную ветку:

SELECT id, amount,
       CASE amount WHEN NULL THEN 'нет суммы' ELSE 'есть' END AS wrong,
       CASE WHEN amount IS NULL THEN 'нет суммы' ELSE 'есть' END AS right_way
FROM orders;
id  amount  wrong  right_way
--  ------  -----  ---------
1   1200    есть   есть
2   450     есть   есть
3   8900    есть   есть
4           есть   нет суммы
5   3100    есть   есть
6   15000   есть   есть

Простой CASE сравнивает значения через знак равенства, а NULL ничему не равен, даже другому NULL. В документации SQLite это сказано прямо: если базовое выражение NULL, результатом всегда будет ELSE или NULL.

Решение: для NULL берите поисковую форму и IS NULL.

Широкое условие стоит выше узкого

SELECT id, amount,
       CASE
           WHEN amount > 1000 THEN 'средний'
           WHEN amount > 5000 THEN 'крупный'
           ELSE 'мелкий'
       END AS size
FROM orders
WHERE amount IS NOT NULL;
id  amount  size
--  ------  -------
1   1200    средний
2   450     мелкий
3   8900    средний
5   3100    средний
6   15000   средний

Заказ на 15 000 попал в «средние»: условие amount > 1000 истинно, до второй ветки дело не дошло. Ошибки СУБД не покажет.

Решение: пишите условия от узкого к широкому или задавайте диапазоны с обеих сторон.

Другие ловушки

  • Забыли END. Без него запрос не выполнится. Чаще всего так бывает, когда CASE копируют из середины другого запроса.
  • Числа и текст в разных ветках. SQLite такое пропускает: ELSE amount рядом с THEN 'нет' выполнился без ошибки. А PostgreSQL и SQL Server требуют общего типа. Приведите ветки сами: CAST(amount AS TEXT) или CAST(amount AS VARCHAR(20)).
  • CASE в хранимой процедуре MySQL. Там это отдельная инструкция, а не выражение: она заканчивается на END CASE, и без ELSE при отсутствии совпадений выдаёт ошибку «Case not found for CASE statement» (справка MySQL).
  • Деление на ноль внутри ветки. CASE обычно не вычисляет лишние ветки, но PostgreSQL оговаривается, что «железной гарантии» нет. В SQL Server агрегаты из WHEN «вычисляются сначала», поэтому в примере из справки MAX(1 / value) падает с делением на ноль, хотя ветка выше уже вернула бы 0. Надёжнее защищаться через NULLIF(делитель, 0).

Вопросы про CASE, NULL и порядок условий регулярно попадают в тестовые задания. Примеры разобраны в статье про собеседование на аналитика данных.

Ваня БуявецКанал основателя Checkroi Вани БуявцаЗабирайте промпты и обучение по нейросетям в моём Телеграм-каналеБольше 3 700 человек уже применяют Claude Code, ChatGPT и другие нейросети в работе, учёбе, бизнесе и жизниПерейти в канал

Где освоить SQL системно

CASE закрывает одну задачу: условную логику внутри запроса. Чтобы уверенно собирать отчёты, нужны ещё JOIN, подзапросы, оконные функции и привычка проверять результат на краевых случаях. Если хотите идти в профессию, вот план, как стать SQL-разработчиком.

Сравнить курсы по SQL по цене и программе можно в каталоге курсов SQL. Ниже пять программ, с которых удобно начать сравнение.

КурсШколаСтоимость со скидкойВ рассрочкуДлитель­ностьОбзор курса от Checkroi
Нейросети для рабочих задач
Перейти на сайт курса
SkillboxSkillbox31 290 ₽ - 1 месяцОбзор курса
SQL для анализа данных
Перейти на сайт курса
SkillboxSkillbox52 353 ₽6203 ₽/мес.2 месяцаОбзор курса
SQL-разработчик: тариф PRO
Перейти на сайт курса
Академия ЭдюсонЭдюсон89 900 ₽7491 ₽/мес.2 месяцаОбзор курса
SQL-разработчик: тариф Базовый
Перейти на сайт курса
Академия ЭдюсонЭдюсон79 900 ₽3329 ₽/мес.3 месяцаОбзор курса
SQL с нуляSkyproSkypro134 640 ₽5500 ₽/мес.12 месяцевОбзор курса

Больше программ — в полном каталоге курсов по SQL

Часто задаваемые вопросы

Что такое CASE в SQL?

Условное выражение, аналог if/else внутри запроса. CASE проверяет условия WHEN по порядку и возвращает значение из THEN первой истинной ветки. Если ни одна не подошла, возвращается значение из ELSE, а без ELSE получится NULL.

Чем простой CASE отличается от поискового?

Простой CASE сравнивает одно выражение со списком значений: CASE status WHEN 'paid' THEN …. Поисковый проверяет любые условия: диапазоны, IS NULL, несколько столбцов сразу. Для проверки на NULL подходит только поисковая форма.

Обязательно ли писать ELSE в CASE?

В запросе нет: без ELSE неподходящие строки получат NULL. Но в UPDATE и в SUM ветка ELSE почти всегда нужна, иначе значения затрутся или сумма станет NULL. В хранимой процедуре MySQL инструкция CASE без ELSE при отсутствии совпадений выдаёт ошибку.

Можно ли использовать CASE в WHERE?

Можно: CASE допустим везде, где допустимо выражение. Но условие в WHERE обычно проще переписать через AND и OR, оно станет короче и понятнее при чтении.

Как посчитать строки по условию через CASE?

Оберните CASE в агрегат: COUNT(CASE WHEN status = 'paid' THEN 1 END) считает оплаченные заказы, SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) складывает их суммы. В PostgreSQL то же самое пишется через COUNT(*) FILTER (WHERE status = 'paid').

Сколько уровней вложенности CASE допускает SQL Server?

Не больше 10, так сказано в справке Microsoft. Функция IIF переводится в CASE и подчиняется тому же ограничению.

Читайте Checkroi первым в Google
Добавьте Checkroi в избранные источники — и наши разборы курсов и обзоры школ будут показываться выше в вашей выдаче Google.
Оставить комментарий
0 комментариев
Форма комментария

Оставьте комментарий

Напишите, что думаете. Нам важно ваше мнение!