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