Агрегация с условиями — классическая задача для отчетности. Изначально для подсчета разных показателей создавались отдельные запросы с фильтрами. Вскоре появилось более компактное решение — условная агрегация с использованием CASE внутри агрегатных функций (например, SUM(CASE WHEN ...)). Проблема проявляется при совмещении фильтров, группировок и заголовков итогов: можно легко получить неверные суммы либо ошибочно интерпретировать результат.
Решение: использовать условную агрегацию внутри агрегатных функций. Например, нужно вывести по всем сотрудникам количество заказов "в обработке" и "завершено":
SELECT employee_id, SUM(CASE WHEN status = 'processing' THEN 1 ELSE 0 END) as processing_count, SUM(CASE WHEN status = 'done' THEN 1 ELSE 0 END) as done_count FROM Orders GROUP BY employee_id;
Ключевые особенности:
Что произойдет, если условие CASE возвращает NULL вместо 0?
Агрегатная функция SUM игнорирует NULL. Поэтому, если писать CASE WHEN ... THEN 1 END, пропущенные строки не учтутся. Лучше всегда явно задавать ELSE 0.
SUM(CASE WHEN status = 'processing' THEN 1 ELSE 0 END)
Может ли WHERE-фильтр изменить итоговую сумму по статусам при условной агрегации?
Да: если основной WHERE ограничивает выборку (например, WHERE region = 'west'), то подсчеты будут вестись только по отфильтрованным данным. Для глобальных итогов используйте подзапрос или убирайте фильтр.
Можно ли использовать HAVING для фильтрации строк до группировки?
Нет. HAVING фильтрует уже сгруппированные данные по агрегатам. Фильтрация исходных строк — через WHERE.
В аналитическом отчете использовали:
SUM(CASE WHEN status = 'approved' THEN 1 END)
Было много NULL, итоговые суммы занижены. Из-за фильтра WHERE часть нужных строк терялась.
Плюсы:
Минусы:
Использовали:
SUM(CASE WHEN status = 'approved' THEN 1 ELSE 0 END)
Код поддерживали в едином запросе, фильтры на итог — через HAVING.
Плюсы:
Минусы: