CASE WHEN: условные выражения
CASE WHEN: условные выражения
В предыдущих уроках вы освоили специальные операторы: LIKE для шаблонов, IN для списков, BETWEEN для диапазонов, IS NULL для проверки на отсутствие значения. Завершим этот модуль мощным инструментом — CASE WHEN. Это аналог конструкции if-else из обычных языков программирования, реализованный прямо внутри SQL. Он позволяет добавить условную логику в SELECT-список, создавая вычисляемые колонки на основе ветвления.
Что такое CASE WHEN
CASE WHEN — это выражение (не оператор!), которое вычисляет разные значения в зависимости от условия. Поскольку это выражение, его можно использовать везде, где допустимо выражение: в SELECT-списке, в ORDER BY, в агрегатных функциях (об этом в модуле 5).
Базовый синтаксис:
CASE
WHEN условие1 THEN результат1
WHEN условие2 THEN результат2
...
ELSE результат_по_умолчанию
END
СУБД проверяет условия сверху вниз. Как только нашлось первое истинное (TRUE) условие — возвращается соответствующий результат, остальные условия не проверяются. Если ни одно условие не истинно — возвращается ELSE. Если ELSE не указан — возвращается NULL.
Простой пример: метки по диапазону
Задача: добавить к товарам текстовую метку цены.
SELECT name,
price,
CASE
WHEN price < 1000 THEN 'бюджетный'
WHEN price < 5000 THEN 'средний'
WHEN price < 20000 THEN 'дорогой'
ELSE 'премиум'
END AS price_category
FROM products;
Результат:
name | price | price_category
------------+-------+---------------
Мышь | 800 | бюджетный
Клавиатура | 2500 | средний
Монитор | 12000 | дорогой
Ноутбук | 55000 | премиум
Каждая строка получила категорию на основе значения price. Важно: условия проверяются по порядку. Для price = 2500 первое условие < 1000 — ложно, второе < 5000 — истинно, возвращается «средний». Дальнейшие условия уже не проверяются.
Проверь себя: какую категорию получит товар с price = 5000?
Синтаксис CASE с равенством (CASE value WHEN)
Есть сокращённый вариант CASE, когда нужно проверять одну колонку на равенство нескольким значениям:
CASE колонка
WHEN значение1 THEN результат1
WHEN значение2 THEN результат2
ELSE результат_по_умолчанию
END
Пример: перевести статус заказа с технического кода на русский:
SELECT order_id,
CASE status
WHEN 'pending' THEN 'Ожидает обработки'
WHEN 'shipped' THEN 'Отправлен'
WHEN 'delivered' THEN 'Доставлен'
WHEN 'cancelled' THEN 'Отменён'
ELSE 'Неизвестный статус'
END AS status_ru
FROM orders;
Эта форма (CASE колонка WHEN значение) — синтаксический сахар для CASE WHEN колонка = значение. Оба варианта дают одинаковый результат, но для простых проверок на равенство короткая форма читабельнее.
CASE WHEN в ORDER BY
CASE WHEN можно использовать в ORDER BY для нестандартной сортировки:
SELECT name, status
FROM orders
ORDER BY
CASE status
WHEN 'urgent' THEN 1
WHEN 'pending' THEN 2
WHEN 'shipped' THEN 3
ELSE 4
END;
Заказы отсортированы в кастомном порядке: сначала срочные, затем ожидающие, затем отправленные, затем всё остальное. Обычная сортировка по строкам дала бы алфавитный порядок — «pending, shipped, urgent» — что не соответствует смысловому приоритету.
CASE WHEN с IS NULL
CASE WHEN прекрасно обрабатывает NULL:
SELECT name,
CASE
WHEN phone IS NULL THEN 'не указан'
ELSE phone
END AS contact_phone
FROM customers;
Вместо пустого поля — понятный текст «не указан». Фактически это то же самое, что COALESCE(phone, 'не указан'), но CASE WHEN гибче: можно написать разную логику для разных условий.
Вложенные условия в CASE
Условие в WHEN может быть произвольным логическим выражением — с AND, OR, NOT, вызовами функций:
SELECT name,
salary,
CASE
WHEN department = 'Разработка' AND salary > 100000 THEN 'Senior Dev'
WHEN department = 'Разработка' AND salary > 60000 THEN 'Dev'
WHEN department = 'Менеджмент' THEN 'Manager'
ELSE 'Другой'
END AS role_label
FROM employees;
Можно комбинировать CASE с другими операторами, которые вы изучили — IN, BETWEEN, LIKE:
SELECT name,
email,
CASE
WHEN email LIKE '%@gmail.com' THEN 'Gmail'
WHEN email LIKE '%@yandex.ru' THEN 'Яндекс'
WHEN email IN (SELECT email FROM corporate_emails) THEN 'Корпоратив'
ELSE 'Другое'
END AS email_provider
FROM customers;
CASE WHEN в WHERE
Технически CASE WHEN можно использовать в WHERE, хотя это встречается реже:
SELECT * FROM products
WHERE
CASE
WHEN category = 'Электроника' THEN price < 50000
WHEN category = 'Одежда' THEN price < 5000
ELSE price < 10000
END;
Это эквивалентно сложному условию с OR, но иногда CASE делает логику нагляднее.
Типичные ошибки
1. Забытый END:
SELECT CASE WHEN price > 1000 THEN 'дорогой'
ELSE 'дешёвый'
-- нет END!
FROM products; -- синтаксическая ошибка
CASE ... END — обязательная пара, как открывающая и закрывающая скобки.
2. Нет ELSE — результат NULL:
SELECT CASE
WHEN status = 'active' THEN 'Активен'
WHEN status = 'banned' THEN 'Заблокирован'
END AS status_label
-- Если status = 'pending' — вернёт NULL
-- Добавьте ELSE:
END AS status_label
-- ELSE 'Другой' END
3. Порядок условий важен:
CASE
WHEN score >= 50 THEN 'Сдал' -- проверяется первым
WHEN score >= 90 THEN 'Отлично' -- никогда не достигается для >= 90!
END
-- Правильно: сначала более узкие условия
CASE
WHEN score >= 90 THEN 'Отлично'
WHEN score >= 50 THEN 'Сдал'
ELSE 'Не сдал'
END
Практический пример: сегментация клиентов
Задача: разделить клиентов на сегменты по количеству заказов:
SELECT customer_id,
name,
order_count,
CASE
WHEN order_count = 0 THEN 'Новый'
WHEN order_count BETWEEN 1 AND 5 THEN 'Редкий'
WHEN order_count BETWEEN 6 AND 20 THEN 'Постоянный'
ELSE 'VIP'
END AS customer_segment
FROM customer_stats
ORDER BY order_count DESC;
Результат: каждый клиент получил сегмент — от «Нового» до «VIP». Это типичная аналитическая задача, где CASE WHEN незаменим.
CASE WHEN как инструмент трансформации данных
Одно из главных применений CASE WHEN — трансформация данных при выгрузке. База данных хранит данные в «технической» форме (коды, флаги, числа), а пользователи хотят видеть «человеческие» метки.
SELECT order_id,
customer_name,
CASE payment_method
WHEN 1 THEN 'Карта'
WHEN 2 THEN 'Наличные'
WHEN 3 THEN 'СБП'
WHEN 4 THEN 'Счёт'
ELSE 'Неизвестно'
END AS payment_label,
CASE
WHEN total_amount >= 10000 THEN 'Крупный'
WHEN total_amount >= 1000 THEN 'Средний'
ELSE 'Мелкий'
END AS order_size,
total_amount
FROM orders
ORDER BY total_amount DESC;
Этот запрос делает выгрузку «человекочитаемой» без изменения данных в базе: метод оплаты отображается текстом, заказ получает метку размера.
CASE WHEN и NULL: полный пример
Обработка NULL-значений — одна из главных причин использовать CASE WHEN:
SELECT
user_id,
CASE
WHEN last_login IS NULL THEN 'Никогда не заходил'
WHEN last_login < NOW() - INTERVAL '365 days' THEN 'Неактивен >1 года'
WHEN last_login < NOW() - INTERVAL '30 days' THEN 'Неактивен >30 дней'
ELSE 'Активный'
END AS activity_status
FROM users;
NOW() — текущий момент времени в PostgreSQL. INTERVAL — интервал времени. Детали работы с датами будут в модуле 9.
Возвращаемый тип CASE WHEN
Все ветки THEN (и ELSE) в одном CASE должны возвращать совместимые типы. Нельзя вернуть строку в одной ветке и число в другой без явного приведения типов:
-- Ошибка: несовместимые типы
CASE WHEN flag = 1 THEN 'да' ELSE 0 END -- строка vs число
-- Правильно: один тип
CASE WHEN flag = 1 THEN 'да' ELSE 'нет' END
CASE WHEN flag = 1 THEN 1 ELSE 0 END
PostgreSQL пытается автоматически привести типы, если они совместимы, но лучше не полагаться на неявное преобразование.
Краткий итог
CASE WHEN условие THEN результат ... END— выражение с ветвлением- Условия проверяются сверху вниз; первое истинное — возвращает результат
ELSE— значение по умолчанию; безELSEпри несовпадении возвращаетсяNULL- Короткая форма:
CASE колонка WHEN значение THEN результат END— для проверок на равенство CASE WHENработает вSELECT,ORDER BY,WHEREи в агрегатных функциях- Порядок условий важен: ставьте более узкие условия выше более широких
- Все ветки
THEN/ELSEдолжны возвращать совместимые типы данных
Что дальше
Вы завершили четвёртый модуль — «Операторы и шаблоны». Вы освоили LIKE, IN, BETWEEN, IS NULL и CASE WHEN. В следующем модуле — агрегатные функции: COUNT, SUM, AVG, MIN, MAX и группировка данных с помощью GROUP BY. Это позволит делать не просто выборку строк, а вычислять итоговые показатели по группам данных.