На каком этапе должно проверяться условие, зависящее от результата агрегирования группы?
Условие, зависящее от результата агрегирования, должно применяться после формирования групп и вычисления агрегатных функций — через HAVING. WHERE работает со строками до группировки и потому не может корректно фильтровать результат COUNT, SUM, AVG и других агрегатов.
Реляционные запросы изначально решали задачу отбора и преобразования множества строк. По мере появления отчётов потребовалось сначала объединять строки в группы, вычислять по ним сводные показатели, а затем отбирать уже готовые группы.
В SQL для этих двух уровней фильтрации используются разные этапы: WHERE ограничивает исходные строки, а HAVING — группы после агрегирования. Такое разделение устраняет неоднозначность между фильтрацией данных и фильтрацией результатов вычислений.
Предположим, нужно найти отделы, в которых больше десяти сотрудников. Число сотрудников не существует как значение отдельной исходной строки: оно появляется только после группировки и применения COUNT.
Если попытаться применить такое условие на этапе WHERE, СУБД не сможет использовать агрегат как обычное построчное значение. Неверное размещение условия приводит либо к синтаксической ошибке, либо к изменению смысла запроса, если вместо агрегированного условия начинают фильтровать отдельные строки.
Логически запрос выполняется в порядке, отличающемся от порядка записи: сначала выбираются источники данных, затем применяются построчные ограничения WHERE, формируются группы по GROUP BY, вычисляются агрегаты и только после этого проверяется HAVING.
Например:
Здесь WHERE исключает неактивных сотрудников до подсчёта. Затем оставшиеся строки группируются по отделу, COUNT(*) вычисляет размер каждой группы, а HAVING оставляет только группы с числом сотрудников больше десяти.
Это важно для смысла результата: условие в WHERE меняет состав строк, участвующих в агрегировании, а условие в HAVING не меняет состав группы, а удаляет группу целиком после расчёта показателя.
Условие, не зависящее от агрегирования, обычно лучше размещать в WHERE. Это делает намерение яснее и часто позволяет СУБД раньше сократить объём обрабатываемых данных, хотя конкретная оптимизация зависит от плана выполнения.
В отчёте нужно показать клиентов, у которых сумма оплаченных заказов за месяц превышает 100 000. Один вариант — отобрать оплаченные заказы в WHERE, сгруппировать их по клиенту и применить порог суммы в HAVING.
Перенос порога суммы в WHERE невозможен по смыслу: на этапе обработки отдельной строки ещё нет общей суммы клиента. Попытка сначала получить суммы во вложенном запросе, а затем фильтровать их во внешнем запросе технически возможна, но добавляет уровень вложенности без необходимости.
Выбранное решение — разделить условия: статус и период применить до группировки, а минимальную сумму — после неё. Это одновременно сохраняет корректную семантику, уменьшает объём группируемых данных и делает запрос понятным при сопровождении.
Ответ зависит от конкретного выражения и правил СУБД. В стандартной логике группированного запроса неагрегированный столбец должен быть однозначно определён группой — обычно он входит в GROUP BY; иначе значение для группы неоднозначно. Некоторые СУБД допускают дополнительные случаи благодаря функциональным зависимостям или нестандартным правилам, но переносимость такого запроса ограничена.
При корректной группировке результат может стать другим. Условие в WHERE исключает отдельные строки до подсчёта, поэтому агрегат считается только по оставшимся строкам; то же условие в HAVING сначала участвует в формировании агрегата косвенно через всю группу, а затем может удалить группу целиком. Перенос допустим только при доказанной эквивалентности, а не как механическое изменение синтаксиса.
Да. Это типичный и полезный сценарий: WHERE фильтрует исходные строки по обычным признакам, затем выполняются группировка и агрегирование, а HAVING фильтрует группы по агрегатным или групповым условиям. Например, статус заказа можно ограничить до группировки, а минимальную сумму заказов клиента — после неё.