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