⏳ Последние дни октября! SQLtest.online бесплатен благодаря донатам, и нам ещё нужно собрать сумму этого месяца, чтобы сайт продолжил работать в ноябре. Важен каждый донат. Поддержать сейчас →
SQL код скопирован в буфер обмена

SQL CASE WHEN: условная логика в запросах

Урок 3.5 · Время чтения: ~7 мин · Задачи для практики ↓

Выражение CASE в SQL добавляет в запрос логику «если - то - иначе»: оно проверяет условия по порядку и возвращает значение первого истинного. В этом уроке вы изучите две формы CASE, научитесь создавать категории и метки в SELECT, сортировать по собственному приоритету и использовать CASE в WHERE.

Функции из предыдущих уроков преобразуют каждую строку одинаково. Но часто правило зависит от данных: платёж меньше $2 - «маленький», больше $6 - «крупный»; аренда без даты возврата - «не возвращена». CASE позволяет записать такие правила прямо в SQL, не перенося их в код приложения.

SQL CASE WHEN: условия проверяются сверху вниз, первое истинное возвращает своё значение, ELSE задаёт значение по умолчанию; CASE используется для меток и своей сортировки

Синтаксис CASE

У CASE две формы.

Поисковый CASE

Каждая ветка WHEN содержит собственное условие. Это более гибкая и самая распространённая форма:

CASE
    WHEN condition1 THEN result1
    WHEN condition2 THEN result2
    ELSE default_result
END

Простой CASE

Одно выражение сравнивается со списком значений:

CASE expression
    WHEN value1 THEN result1
    WHEN value2 THEN result2
    ELSE default_result
END

Как вычисляется CASE

  • Условия проверяются сверху вниз.
  • Возвращается результат первого истинного условия; остальные уже не проверяются.
  • Если ни одно условие не истинно, возвращается значение из ELSE.
  • Если ELSE нет, результат - NULL.

CASE - это выражение, а не отдельный оператор: оно возвращает одно значение и может стоять везде, где допустимо значение, - в SELECT, WHERE или ORDER BY.


CASE в SELECT: категории и метки

Уровни платежей

SELECT payment_id,
       amount,
       CASE
           WHEN amount < 2 THEN 'Low'
           WHEN amount < 6 THEN 'Medium'
           ELSE 'High'
       END AS payment_level
FROM payment
LIMIT 10;

Результат: десять платежей, у каждого уровень: меньше $2 - «Low», от $2 до $6 - «Medium», от $6 и выше - «High».

Порядок условий важен. Платёж в $1 удовлетворяет и условию amount < 6, но получает «Low», потому что побеждает первая подходящая ветка. Благодаря этому второму условию не нужна нижняя граница.

Понятный статус

SELECT rental_id,
       rental_date,
       CASE
           WHEN return_date IS NULL THEN 'Not returned'
           ELSE 'Returned'
       END AS rental_status
FROM rental
ORDER BY rental_date DESC
LIMIT 10;

Результат: последние аренды с текстовым статусом вместо пустой или заполненной даты возврата.

Простой CASE для кодов

Простая форма удобна, когда в столбце хранятся коды:

SELECT title,
       rating,
       CASE rating
           WHEN 'G' THEN 'General audiences'
           WHEN 'PG' THEN 'Parental guidance'
           WHEN 'PG-13' THEN 'Parents strongly cautioned'
           WHEN 'R' THEN 'Restricted'
           ELSE 'Adults only'
       END AS rating_description
FROM film
LIMIT 10;

Результат: фильмы с понятным описанием их рейтинга.


CASE в ORDER BY: собственный порядок сортировки

Алфавитный порядок кодов рейтинга (G, NC-17, PG, PG-13, R) не совпадает с их смыслом. CASE превращает каждый код в число-приоритет:

SELECT title, rating
FROM film
ORDER BY CASE rating
             WHEN 'G' THEN 1
             WHEN 'PG' THEN 2
             WHEN 'PG-13' THEN 3
             WHEN 'R' THEN 4
             ELSE 5
         END,
         title;

Результат: фильмы от самого мягкого рейтинга к самому строгому; внутри рейтинга - по названию.

Тот же приём поднимает наверх важные строки:

SELECT rental_id, rental_date, return_date
FROM rental
ORDER BY CASE WHEN return_date IS NULL THEN 0 ELSE 1 END,
         rental_date DESC
LIMIT 20;

Результат: сначала невозвращённые аренды, затем остальные; в каждой группе новые - первыми.


CASE в WHERE

CASE может вернуть значение для условия, например порог, зависящий от другого столбца:

SELECT payment_id, staff_id, amount
FROM payment
WHERE amount >= CASE
                    WHEN staff_id = 1 THEN 5
                    ELSE 3
                END
LIMIT 10;

Результат: платежи не меньше $5 для сотрудника 1 и не меньше $3 для остальных.

Большинство таких фильтров можно записать и через AND и OR: WHERE (staff_id = 1 AND amount >= 5) OR (staff_id <> 1 AND amount >= 3). Выбирайте вариант, который легче читается: для двух веток часто это OR, для многих - CASE.


Практические советы

  • Всегда пишите ELSE. Без него неожиданные значения превращаются в NULL, и такие строки легко пропустить.
  • Возвращайте один тип данных. Смешивание текста и чисел в разных ветках ведёт к неявным преобразованиям и сюрпризам.
  • Располагайте условия от частных к общим. Побеждает первая истинная ветка.
  • В уроке 4.4 вы будете использовать CASE внутри агрегатных функций, чтобы одним запросом считать строки по категориям.

Ключевые выводы:

  • CASE возвращает результат первого истинного условия WHEN или ELSE, если ни одно не истинно.
  • Поисковый CASE проверяет любые условия; простой CASE сравнивает одно выражение со значениями.
  • Без ELSE результат - NULL.
  • В SELECT CASE создаёт категории и понятные метки.
  • В ORDER BY он задаёт собственный порядок сортировки, а в WHERE может сделать фильтр зависимым от других столбцов.

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

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

Простой CASE сравнивает одно выражение со списком значений: CASE rating WHEN 'G' THEN .... Поисковый CASE проверяет отдельное условие в каждой ветке: CASE WHEN amount < 2 THEN .... Поисковый CASE умеет всё, что простой, а также диапазоны, проверки на NULL и условия по нескольким столбцам.

Что вернёт CASE, если ни одно условие не истинно и ELSE нет?

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

Есть ли в SQL функция IF?

Стандартный способ - CASE. В MySQL и MariaDB есть также IF(condition, value_if_true, value_if_false), а в SQL Server - IIF(), но они не переносимы.

Попробуйте решить следующие задачи, чтобы закрепить материал этого урока.

  1. Подчститайте ежедневное количество рейсов
  2. Корректировка стоимости аренды