Я много лет страстно увлечен базами данных и программированием на языке SQL. Поверьте, это просто и увлекательно, к тому же высокооплачиваемое умение.
Отвлекитесь ненадолго и полистайте мои практические беседы без теоретической шелухи. Пусть вас не удивляет нумерация разделов - я постарался изложить материал в наиболее полезной последовательности.
Содержание беседы #6:
Тема 2. Язык структурированных запросов
Раздел 2.1 Понятие Стандарта ANSI-SQL
Пункт 2.1.2 Основные команды языка (Расширенное понимание Group By, Having)
Мы уже рассматривали назначение различных агрегатов (получение суммы по полю или же количества записей). Однако цель у агрегата всегда одна – вычислить агрегирующие значения по одной или нескольким группам. Типичная задача звучит так: Вычислить для каждого Предмета что-то (не «вывести предметы такие-то», а именно "вычислить").
Рассмотрим таблицу «Продажи» с полями: название филиала, дата отчетного дня, сумма дохода.
Задача №1: вычислить для каждого филиала сумму дохода за всё время.
Select FiLiaL, sum(SumDohod)
From Prodazhi
Group by FiLiaL
Задача №2: вывести суммы общего дохода только по тем филиалам, где эта сумма более 1 млн.
Select FiLiaL, sum(SumDohod)
From Prodazhi
Group by FiLiaL
Having sum(SumDohod) >1000000
Задача №3: вывести данные по всем филиалам за дни, когда прибыль была выше средней суммы дохода за день по этому филиалу.
Select A.FiLiaL, A.Date, A.SumDohod
From Prodazhi as A
Where A.SumDohod > (Select Avg(B.SumDohod)
From Prodazhi as B
Where B.FiLiaL = A.FiLiaL
)
Теперь самое любопытное: использование подзапроса с агрегатом в разделе Having.
Задача №4: вывести сумму общего дохода только для тех филиалов, которые работали в последний отчетный день.
Есть желание решить задачу объединением решений первой и второй задач:
Select FiLiaL, sum(SumDohod)
From Prodazhi as A
Where A.Date = (Select Max(B.Date)
From Prodazhi as B)
Group by FiLiaL
Однако это неверно. Потому что мы возьмем только выручку последнего дня. Мы не можем поставить ограничение на дату в разделе Where. Именно для решения таких задач и есть как бы еще один Where уже после подсчетов по группировке – Having, где нам никто не мешает вместо значения поставить целый подзапрос с агрегатом. Правильное решение следующее:
Select A.FiLiaL, Sum(A.SumDohod)
From Prodazhi as A
Group by A.FiLiaL
Having Max(A.Date) = (Select Max(B.Date)
From Prodazhi as B)
Итоговые замечания по агрегатам.
Агрегат может быть использован и без группировки: когда нужна общая цифра без выделения отдельных значений группы. Чаще всего подзапрос с агрегатом без группировки используется именно в разделе Where.
И наоборот, группировка может быть использована без агрегата – результат идентичен опции Distinct.
Агрегаты и группировки позволяют использовать в логических условиях данные, которые в явном виде в базе не хранятся, а существуют только как вычислимые статистические показатели.
Не страшно, если после прочтения беседы останется чувство недопонимания. Пожалуй, агрегаты - самая сложная тема при базовом овладении языком. Чтобы обнадежить вашу бороду, скажу, что лично знаю как минимум одного хорошего программиста SQL, который вовсе не использует операторы group by и having в работе, поскольку до конца их не понимает. И он успешно обходится без них.
Спасибо, что дочитали до конца! Любите друг друга... и базы данных)
Автор: Щукин Андрей (vk.com/faustonly), Санкт-Петербург. Дата публикации: 01.05.2018