- Вычислять итоги по каждой группе с помощью
GROUP BY - Объяснять разницу между
WHEREиHAVING - Знать логический порядок выполнения частей запроса
Директор спрашивает: «Сколько у нас учеников из каждого города?» Из прошлого урока мы знаем, как посчитать один город с помощью COUNT(*) и WHERE city = 'Bakı'. Но писать семь запросов для семи городов? Нет — GROUP BY отвечает сразу за все города одним запросом.
GROUP BY: разбиваем строки на группы
GROUP BY столбец собирает строки с одинаковым значением в одну группу, а агрегатная функция вычисляется для каждой группы отдельно. В результате получается одна строка на группу.
SELECT city, COUNT(*) AS students
FROM students
GROUP BY city
ORDER BY students DESC, city;▸ Ожидаемый результат
city | students Bakı | 4 Gəncə | 2 Sumqayıt | 2 Lənkəran | 1 Naxçıvan | 1 Quba | 1 Şəki | 1
FROM studentsберёт 12 строк таблицы.GROUP BY cityскладывает строки с одинаковым городом в одну «корзину» — получается 7 корзин.COUNT(*)считает строки в каждой корзине.- Каждая корзина превращается в одну строку: город и количество.
Для группы можно вычислить и несколько агрегатов. Ниже для каждой категории — количество товаров, средняя цена и общий остаток на складе.
SELECT category,
COUNT(*) AS products,
ROUND(AVG(price), 2) AS avg_price,
SUM(stock) AS total_stock
FROM products
GROUP BY category
ORDER BY category;▸ Ожидаемый результат
category | products | avg_price | total_stock Accessories | 2 | 33.5 | 180 Electronics | 4 | 695.12 | 63 Games | 1 | 42 | 18 Home | 1 | 34.99 | 25 Stationery | 2 | 5.55 | 800
Группировка по нескольким столбцам и выражениям
Если в GROUP BY несколько столбцов, группу определяет сочетание значений. Например, посчитаем покупателей для каждой пары «страна — город».
SELECT country, city, COUNT(*) AS customers
FROM customers
GROUP BY country, city
ORDER BY country, city;▸ Ожидаемый результат
country | city | customers Azerbaijan | Bakı | 2 Azerbaijan | Gəncə | 1 Russia | Moscow | 1 Türkiye | Ankara | 1 Türkiye | İstanbul | 1 United Kingdom | London | 1
Группировать можно и по выражению. SUBSTR(order_date, 1, 7) вырезает из даты первые 7 символов — год и месяц (2025-04), — и так мы считаем заказы по месяцам. В SQL Server эта функция называется SUBSTRING.
SELECT SUBSTR(order_date, 1, 7) AS month,
COUNT(*) AS orders
FROM orders
GROUP BY SUBSTR(order_date, 1, 7)
ORDER BY month;▸ Ожидаемый результат
month | orders 2025-01 | 2 2025-02 | 2 2025-03 | 2 2025-04 | 2 2025-05 | 1 2025-06 | 2 2025-07 | 1
HAVING: фильтруем группы
А если нужны только города, где не меньше двух учеников? Написать WHERE COUNT(*) >= 2 нельзя: WHERE смотрит на отдельные строки до образования групп, когда ещё ничего не посчитано. Условия для групп пишут в HAVING — он работает после группировки.
SELECT city, COUNT(*) AS students
FROM students
GROUP BY city
HAVING COUNT(*) >= 2
ORDER BY city;▸ Ожидаемый результат
city | students Bakı | 4 Gəncə | 2 Sumqayıt | 2
SELECT course_id, ROUND(AVG(score), 1) AS avg_score
FROM enrollments
GROUP BY course_id
HAVING AVG(score) >= 85
ORDER BY avg_score DESC;▸ Ожидаемый результат
course_id | avg_score 6 | 92.7 5 | 90.5 1 | 87.3
| Критерий | WHERE | HAVING |
|---|---|---|
| Что фильтрует | отдельные строки | группы |
| Когда работает | до группировки | после группировки |
| Можно ли агрегаты? | нет | да |
WHERE и HAVING могут работать в одном запросе вместе. Ниже WHERE сначала отбрасывает товар, которого нет на складе (Monitor), затем оставшиеся делятся на категории, а HAVING оставляет только категории, где больше одного товара.
SELECT category, COUNT(*) AS in_stock
FROM products
WHERE stock > 0
GROUP BY category
HAVING COUNT(*) > 1
ORDER BY category;▸ Ожидаемый результат
category | in_stock Accessories | 2 Electronics | 3 Stationery | 2
Порядок выполнения запроса
Мы пишем запрос в одном порядке, а СУБД логически выполняет его в другом. Знание этого порядка объясняет многие ошибки — например, почему агрегаты и псевдонимы не работают в WHERE, но работают в ORDER BY.
FROM— берутся строки таблицыWHERE— строки фильтруютсяGROUP BY— строки делятся на группыHAVING— фильтруются группыSELECT— вычисляются столбцы и псевдонимыORDER BY— результат сортируетсяLIMIT— берётся нужное число строк
Какие покупатели купили во всех своих заказах в сумме не меньше 5 штук товара?
Показать решениеСкрыть решение
GROUP BY customer_id.2) Суммируем количество в каждой группе:
SUM(quantity).3) Фильтруем группы:
HAVING SUM(quantity) >= 5.Ответ: покупатель № 2 (21 штука) и покупатель № 1 (13 штук).
SELECT customer_id, SUM(quantity) AS items
FROM orders
GROUP BY customer_id
HAVING SUM(quantity) >= 5
ORDER BY items DESC;▸ Ожидаемый результат
customer_id | items 2 | 21 1 | 13
orders с customers — этому посвящён следующий урок.Выведи для каждого предмета (subject) из courses число курсов (courses) и сумму кредитов (credits). Отсортируй по названию предмета.
SELECT subject
FROM courses
ORDER BY subject;▸ Ожидаемый результат
subject | courses | credits Chemistry | 1 | 3 English | 1 | 2 History | 1 | 2 Informatics | 1 | 3 Math | 2 | 7 Physics | 1 | 4
Найди учеников, у которых средний балл выше 85: student_id и средний балл, округлённый до 1 знака после запятой (avg_score). Отсортируй по среднему баллу по убыванию, а при равенстве — по student_id.
SELECT student_id, ROUND(AVG(score), 1) AS avg_score
FROM enrollments
-- group and filter the groups▸ Ожидаемый результат
student_id | avg_score 11 | 95.5 3 | 92.5 10 | 91 1 | 90 5 | 89 6 | 88
Главное
GROUP BYобъединяет строки с одинаковыми значениями; в результате — одна строка на группу.- Каждый столбец в
SELECTдолжен быть вGROUP BYили внутри агрегата. WHEREфильтрует строки до группировки, аHAVING— группы после неё.- Условия с агрегатами (
COUNT(*) >= 2) пишут только вHAVING. - Логический порядок:
FROM→WHERE→GROUP BY→HAVING→SELECT→ORDER BY→LIMIT.
Проверь себя
Вопросов: 10. Каждый правильный ответ приносит XP.
GROUP BY city в запросе SELECT city, COUNT(*) FROM students GROUP BY city?