АналитикаАнализ данныхАналитик данных

Сравните фильтрацию строк до группировки с фильтрацией групп после агрегации: когда перенос условия меняет ...

Сравните фильтрацию строк до группировки с фильтрацией групп после агрегации: когда перенос условия меняет результат?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

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

Исторический контекст

Разделение фильтрации строк и групп появилось из-за различий между реляционными операциями и агрегированием. Сначала нужно определить набор строк, участвующих в расчёте, затем сформировать группы и вычислить их показатели.

В SQL для этих этапов обычно используются WHERE и HAVING. Такое разделение позволяет явно выразить, фильтруются ли исходные записи или результаты группировки.

Постановка проблемы

Допустим, нужно найти клиентов, у которых суммарная выручка превышает 100. Если отфильтровать отдельные заказы с суммой больше 100 до группировки, заказы меньшего размера исчезнут из расчёта. В результате будет посчитана не полная выручка клиента, а сумма только выбранных заказов.

Обратная ошибка также возможна: условие по отдельной строке нельзя корректно применить после группировки без изменения смысла. Неверное расположение фильтра может привести к потере данных, завышению или занижению агрегатов и неправильным бизнес-выводам.

Подробное решение

WHERE применяется к отдельным строкам до группировки. Поэтому условие в нём должно проверять свойства исходной записи, например дату заказа, статус или сумму конкретного заказа.

HAVING применяется к группам после выполнения GROUP BY. Он предназначен для условий по агрегатам: суммарной выручке, количеству заказов, среднему значению и другим рассчитанным показателям.

SELECT customer_id, SUM(amount) AS revenue FROM orders WHERE status = 'paid' GROUP BY customer_id HAVING SUM(amount) > 100;

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

Условия по ключам группировки иногда можно перенести из HAVING в WHERE без изменения результата. Например, фильтр по региону клиента безопаснее применить до группировки: это уменьшит объём обрабатываемых данных. Но условие по SUM, COUNT или AVG нельзя перенести в WHERE, потому что на этом этапе агрегат ещё не вычислен.

Важен и смысл самого условия. WHERE amount > 100 означает «учитывать только заказы дороже 100», а HAVING SUM(amount) > 100 — «оставить клиентов, чья полная сумма превышает 100». Это разные бизнес-метрики, даже если в тексте используется похожий порог.

Ситуация из практики

Аналитик оценивал клиентов с выручкой более 10 000 рублей. В первом варианте он отфильтровал заказы дороже 10 000 до группировки. Такой вариант прост и быстрее обрабатывается, но исключает множество небольших заказов и не отвечает на поставленный вопрос.

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

Был выбран второй вариант, поскольку бизнес-условие относилось к общей выручке клиента. Предварительный фильтр по статусу оплаты сохранили в WHERE, так как неоплаченные заказы не должны участвовать в расчёте. В результате метрика стала соответствовать определению, а число ошибочно включённых клиентов уменьшилось.

Что кандидаты часто упускают

  1. Можно ли заменить условие по агрегату фильтрацией исходных строк с тем же порогом?

Нет. Условие SUM(amount) > 100 проверяет итог группы, а фильтрация amount > 100 проверяет каждую строку отдельно. Клиент с двумя заказами по 60 должен пройти первое условие, но будет исключён вторым.

  1. Почему фильтрация по статусу заказа обычно должна выполняться до группировки?

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

  1. Всегда ли перенос условия из фильтра групп в фильтр строк безопасен для производительности и результата?

Нет. Перенос безопасен только при сохранении логики запроса, например когда условие зависит от ключа группировки и не меняет набор строк внутри группы. Условия по агрегатам переносить нельзя. Даже корректная оптимизация должна учитывать NULL, дубликаты, тип соединения и точное определение метрики.