Как считать несколько показателей через SUM(CASE WHEN)

Шесть заказов, четыре доставки и одна отмена. Разберём, как получить сводку одним запросом и не превратить ожидаемый ноль в NULL.

SQL Studio / Разборы SQL / Как считать несколько показателей через SUM(CASE WHEN)

Условная агрегация: SUM(CASE WHEN условие THEN число ELSE 0 END) суммирует значение только для подходящих строк. Если требуется количество подходящих строк, суммируйте единицы.

1. Условные счётчики за один запрос

В учебной базе шесть заказов: четыре доставленных, один отменённый, один в обработке.

SELECT COUNT(*) AS orders_total,
       SUM(CASE WHEN status = 'delivered' THEN 1 ELSE 0 END) AS delivered_n,
       SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END) AS cancelled_n
FROM orders;

Результат: 6 заказов всего, 4 доставки и 1 отмена. Разница не потерялась: один заказ ещё находится в обработке.

2. Условная сумма денежных показателей

Стоимость заказа хранится в копейках. Сначала суммируем, затем делим на 100.

SELECT
  SUM(CASE WHEN status = 'delivered' THEN gross_kopecks ELSE 0 END) / 100.0 AS delivered_gross_rub,
  SUM(CASE WHEN status = 'cancelled' THEN gross_kopecks ELSE 0 END) / 100.0 AS cancelled_gross_rub
FROM orders;
Валовая стоимость заказов по статусам, ₽
ДоставленныеОтменённые
1250450

Это стоимость заказов, а не чистые поступления. Для финансового отчёта нужно отдельно проверить платежи и возвраты, иначе можно получить правдоподобную, но неправильную сумму.

3. LEFT JOIN и клиенты с нулём заказов

Хотим увидеть всех клиентов. Считаем заказы по o.order_id, а не через COUNT(*): последний даст единицу для клиента без заказов.

SELECT c.customer_id, c.name,
       COUNT(o.order_id) AS orders_n,
       SUM(CASE WHEN o.status = 'delivered' THEN 1 ELSE 0 END) AS delivered_n
FROM customers AS c
LEFT JOIN orders AS o ON o.customer_id = c.customer_id
GROUP BY c.customer_id, c.name
ORDER BY c.customer_id;

Анна — (2 заказа, 2 доставки), Борис — (2, 1), Вера — (2, 1), Данил — (0, 0). Условный подсчёт не теряет клиента без заказов.

4. Что произойдёт, если забыть ELSE 0

SELECT
  SUM(CASE WHEN status = 'returned' THEN 1 END) AS wrong_null,
  SUM(CASE WHEN status = 'returned' THEN 1 ELSE 0 END) AS correct_zero
FROM orders;

Ответ: wrong_null = NULL и correct_zero = 0. Если подходящих строк нет, SUM из одних NULL возвращает NULL. Если исходная таблица совсем пуста, даже ELSE 0 не поможет — используйте COALESCE(SUM(...),0).

По теме: COUNT и NULL · JOIN без двойного подсчёта · конверсия.

Справка: SQLite — CASE и агрегатные функции.


Для последовательной практики на MySQL 8 — курс «SQL для аналитика» на Stepik с итоговой работой и возможностью получить сертификат.