DML. HAVING. HAVING и WHERE
Дорогу осилит идущийВ рамках сегодняшнего урока мы завершим тему агрегации данных в SQL, рассмотрев механизм ограничения уже агрегированной выборки.
Суть довольно проста: когда мы агрегируем данные с группировкой по каким-то признакам, вполне вероятна ситуация, в которой нас интересует только часть групп.
Довольно наглядным будет пример с поиском дубликатов.
Скажем, мы хотим узнать, есть ли в нашем списке пассажиров тезки и если есть – какие у них имена. По сути, для этого достаточно посчитать, сколько носителей каждого из имен присутствует в таблице:
select count(*), first_name from passenger group by first_name;
Но в результате будут не только имена тезок, но и имена, которые имеют лишь по одному носителю. Соответственно, нам нужна фильтрация результатов.
WHERE-условие для этой цели не подойдет – как выяснили в предыдущем уроке, WHERE отрабатывает до агрегации данных, а нам нужна фильтрация самих результатов агрегации.
Для подобных целей существует оператор HAVING:
select count(*), first_name from passenger group by first_name having count(*) > 1;
Вуаля! В итоговой выборке присутствуют лишь имена с более чем двумя носителями.
Обычно этого достаточно, но мы можем пойти дальше – нам не требуется получать число носителей каждого имени, достаточно обычного перечисления этих имен:
select first_name from passenger group by first_name having count(*) > 1;
Как видите, агрегатная функция исчезла из выражений SELECT. Но секция GROUP BY остается – нам все еще требуется группировка для агрегатной функции в HAVING.
Также в SELECT и HAVING могут использоваться разные агрегатные функции – скажем, агрегация через count() в SELECT и ограничение по max() в HAVING. Единственное ощутимое ограничение – агрегатная функция не может быть вызвана внутри другой агрегатной функции. Т.е. записать в HAVING что-то вроде max(count(*)) НЕ получится.
Еще один момент, который стоит помнить при работе с блоком HAVING – все, что можно вынести из HAVING в WHERE – должно быть вынесено в WHERE. Из-за того, что HAVING работает с результатами агрегации, расположение ограничений в этом блоке делает более тяжелым запрос целиком – ведь приходится агрегировать больший массив данных, чем был бы, перенеся мы HAVING-условия в WHERE.
Скажем, если вы хотите исключить из запроса выше всех Иванов – стоит добавить first_name != 'Ivan' в WHERE. Ведь нам нет необходимости знать число Иванов, чтобы исключить из выборки, следовательно, нет необходимости агрегировать Иванов.
И, по традиции, пример с взаимным расположением блоков в запросе:
select count(*), male from passenger where last_purchase is not null group by male having count(*)> 1 order by male limit 1 offset 0;
С теорией на сегодня все!

Переходим к практике:
Задача
Получите число пассажиров по длине имени. Нас интересуют только короткие имена (меньше 6 символов). Если пассажиров с заданной длиной имени меньше, чем два – игнорируйте их в результирующей выборке. Отсортируйте данные по количеству пассажиров.
Если что-то непонятно или не получается – welcome в комменты к посту или в лс:)
Канал: https://t.me/ViamSupervadetVadens
Мой тг: https://t.me/ironicMotherfucker
Дорогу осилит идущий!