SQL
SQL Введение·Глава 09

Часть бесплатной книги «SQL Введение» · Перейти к содержанию

Группировка данных (Оператор GROUP BY)

Группировка в SQL позволяет объединять строки таблицы по определенному полю или списку полей, чтобы вычислить для них общие (агрегированные) значения: сумму, среднее, максимум, минимум или количество.

Чтобы сгруппировать данные по определенному столбцу, используется ключевое слово  GROUP BY . Оно практически всегда работает в связке с агрегатными функциями:

  • COUNT  — подсчитывает количество строк.
  • AVG  — вычисляет среднее арифметическое.
  • SUM  — считает сумму значений.
  • MAX  — находит максимальное значение.
  • MIN  — находит минимальное значение.

Давай разберем, как анализировать показатели пользователей из нашей таблицы players с помощью группировки.

Среднее значение и округление (AVG, ROUND)

Представим, что разработчику нужно узнать, в каких городах живут самые опытные пользователи. Сгруппируем игроков по городам (city) и рассчитаем средний уровень (level) персонажей.

Наш запрос:

SELECT city,
       AVG(level) AS avg_level
FROM players
GROUP BY city;

Результат (срез, первые 5 строк):

Поскольку среднее значение часто получается дробным с длинным хвостом, результат может выглядеть неаккуратно:

city avg_level
Москва 43.5000
Санкт-Петербург 51.5000
Новосибирск 89.0000
Екатеринбург 54.0000
Омск 55.0000

Чтобы привести отчет в красивый вид, используем функцию  ROUND . Она принимает два аргумента: что округлять и до скольки знаков после запятой.

Доработаем запрос:

SELECT city,
       ROUND(AVG(level), 1) AS avg_level
FROM players
GROUP BY city;

Результат (срез, первые 5 строк):

city avg_level
Москва 43.5
Санкт-Петербург 51.5
Новосибирск 89.0
Екатеринбург 54.0
Омск 55.0

Подсчет количества строк (COUNT)

Чтобы узнать, насколько равномерно распределена наша аудитория по игровым рангам, посчитаем количество игроков в разрезе каждого звания (rank_title).

Наш запрос:

SELECT rank_title,
       COUNT(id) AS players_count
FROM players
GROUP BY rank_title;

Результат:

rank_title players_count
Gold 16
Bronze 8
Diamond 3
Silver 13
Platinum 9
Master 1

Фильтрация групп (Оператор HAVING)

Что делать, если нам интересны только многочисленные ранги (например, где состоит больше 10 игроков)? Обычный оператор WHERE здесь не поможет, потому что он работает с отдельными строками еще до того, как они объединились в группы.

Для фильтрации уже сгруппированных данных используется оператор  HAVING .

Наш запрос:

SELECT rank_title,
       COUNT(id) AS players_count
FROM players
GROUP BY rank_title
HAVING COUNT(id) > 10;

Результат:

Группы с рангом Diamond автоматически отсеялись:

rank_title players_count
Gold 16
Silver 13

Сортировка сгруппированных данных

Итоговый отчет можно дополнительно отсортировать по алфавиту или по результату вычислений с помощью уже знакомого оператора ORDER BY, который пишется в самом конце запроса.

Наш запрос:

SELECT rank_title,
       COUNT(id) AS players_count
FROM players
GROUP BY rank_title
HAVING COUNT(id) > 10
ORDER BY players_count DESC;

Результат:

rank_title players_count
Gold 16
Silver 13

Поиск максимума и минимума (MAX, MIN)

Давай посмотрим на распределение соревновательного рейтинга (rating) внутри каждой гильдии (guild). Выведем одновременно максимальный и минимальный показатель рейтинга для каждого клана:

Наш запрос:

SELECT guild,
       MAX(rating) AS max_rating,
       MIN(rating) AS min_rating
FROM players
WHERE guild IS NOT NULL
GROUP BY guild;

Результат:

guild max_rating min_rating
Грифоны Эрафии 2800 1750
Черные Драконы Нигона 3600 2350
Золотые Единороги Авли 2200 1200
Церберы Инферно 2950 1600
Гидры Таталии 2150 1450
Громовые Птицы Крюлода 2600 2500
Фениксы Конфлюкса 3100 1250
Древние Бегемоты 950 950
Мантикоры Подземелья 2850 2750

Группировка по нескольким полям

В SQL можно разделять данные на более мелкие подгруппы, указав несколько полей через запятую.

Например, давай узнаем, сколько игроков определенного уровня зарегистрировалось в конкретные дни. Сгруппируем таблицу по дате регистрации (registration_date) и уровню (level):

Наш запрос:

SELECT registration_date,
       level,
       COUNT(id) AS players_count
FROM players
GROUP BY registration_date,
         level;

Результат (срез, первые 5 строк):

registration_date level players_count
2025-01-15 45 1
2026-05-10 12 1
2024-08-22 89 1
2025-11-03 31 1
2025-03-14 55 1

Разница между WHERE и HAVING: золотое правило

Главное отличие, которое нужно запомнить раз и навсегда:

WHERE

Выполняет фильтрацию ДО группировки.
Он отбирает отдельные строки таблицы.

HAVING

Выполняет фильтрацию ПОСЛЕ группировки.
Он работает с уже сформированными группами и чаще
всего используется с агрегатными функциями.