Оператор HAVING
Мы уже рассматривали запрос получения средней стоимости аренды жилых помещений в зависимости от типа жилья:
MySQL 8.4SELECT home_type, AVG(price) as avg_price FROM Rooms GROUP BY home_type
Давайте доработаем этот запрос таким образом, чтобы в итоговой выборке отображались только те группы, чья средняя стоимость больше 50.
Обладая предыдущим опытом, есть большой соблазн попытаться использовать для этих целей оператор WHERE. Но при попытке выполнить такой запрос СУБД неминуемо выдаст ошибку, указав, что мы некорректно используем синтаксис WHERE avg_price > 50.
MySQL 8.4SELECT home_type, AVG(price) as avg_price FROM Rooms GROUP BY home_type WHERE avg_price > 50
Говоря наперёд, для фильтрации групп мы должны использовать оператор HAVING:
MySQL 8.4SELECT home_type, AVG(price) as avg_price FROM Rooms GROUP BY home_type HAVING avg_price > 50
MySQL 8.4SELECT home_type, AVG(price) as avg_price FROM Rooms GROUP BY home_type HAVING AVG(price) > 50
Порядок выполнения SQL запроса
Но почему же мы не могли использовать WHERE, и зачем нужен отдельный оператор для этой цели? Все дело в порядке выполнения SQL запроса.

Наш первый запрос был неверный, потому что мы пытались использовать поле avg_price у образовавшихся групп ещё до их образования, так как выполнение оператора WHERE предшествует группировке.
То есть оператор WHERE в момент его исполнения ничего не знает о последующей группировке, он работает только с записями из таблицы. Так мы, например, с его помощью можем отфильтровать записи таблицы Rooms по цене до применения группировки и лишь затем вычислить среднюю стоимость групп оставшегося жилья:
MySQL 8.4SELECT home_type, AVG(price) as avg_price FROM Rooms WHERE price > 50 GROUP BY home_type
Общая структура запроса с оператором HAVING
SELECT поля_выборки
FROM имя_таблицы
[WHERE условие_для_строк]
GROUP BY поле_группировки [, ...]
HAVING условие_для_групп
[ORDER BY поле_сортировки];- [ ]необязательная часть
- ...можно повторить
- поля_выборки
Что вывести для каждой группы: литералы, агрегатные функции и поля группировки.
- имя_таблицы
- Таблица, из которой берутся строки.
- условие_для_строк
Отбирает строки таблицы до группировки. Агрегатные функции в нём использовать нельзя.
Необязательный- поле_группировки
Столбец, по которому строки делятся на группы. Можно указать несколько столбцов через запятую.
- условие_для_групп
Отбирает группы после группировки, например COUNT(*) >= 5. В нём можно использовать агрегатные функции.
- поле_сортировки
Столбец или выражение, по которому сортируется результат.
Необязательный
Пример использования HAVING
Для примера давайте получим минимальную стоимость каждого типа жилья c телевизором. При этом нас интересуют только типы жилья, содержащие как минимум 5 жилых помещений, относящихся к ним.
Чтобы получить такой результат, мы должны:
-
Сначала получить все данные из таблицы
MySQL 8.4SELECT ... FROM Rooms; -
Затем выбрать из всех записей таблицы Room только интересующие нас, т.е. только жильё с телевизором
MySQL 8.4SELECT ... FROM Rooms WHERE has_tv = true -
Затем сгруппировать данные записи о жилых помещениях по их типу
MySQL 8.4SELECT ... FROM Rooms WHERE has_tv = true GROUP BY home_type -
После этого отфильтровать полученные группы по условию. Нас интересуют группы, имеющие как минимум 5 представителей
MySQL 8.4SELECT ... FROM Rooms WHERE has_tv = true GROUP BY home_type HAVING COUNT(*) >= 5 -
И под конец посмотреть, что нас просят в задании и, соответственно, добавить вывод необходимой информации. В нашем случае, нам необходимо вывести название типа жилья и его минимальную стоимость.
MySQL 8.4SELECT home_type, MIN(price) as min_price FROM Rooms WHERE has_tv = true GROUP BY home_type HAVING COUNT(*) >= 5;