SQL GROUP BY

GROUP BY в SQL: как группировать строки и применять агрегатные функции COUNT, SUM, AVG — с примерами на реальных таблицах.

GROUP BY объединяет строки с одинаковым значением в указанном столбце в группы, чтобы агрегатные функции (COUNT, SUM, AVG, MIN, MAX) вычислялись по каждой группе отдельно, а не по всей таблице сразу.

Синтаксис

SELECT столбец, АГРЕГАТНАЯ_ФУНКЦИЯ(...)
FROM имя_таблицы
[WHERE условие]
GROUP BY столбец
[ORDER BY ...];

Пример 1: COUNT сотрудников по отделам

Хотим узнать, сколько сотрудников в каждом отделе — для больших таблиц вручную не посчитаешь:

SELECT d.dept_name, COUNT(e.emp_id) AS total_employees
FROM departments d
LEFT JOIN employees e ON d.dept_id = e.dept_id
GROUP BY d.dept_name;

Результат:

+------------------+-----------------+
| dept_name        | total_employees |
+------------------+-----------------+
| Administration   |               1 |
| Customer Service |               0 |
| Finance          |               1 |
| Human Resources  |               1 |
| Sales            |               1 |
+------------------+-----------------+

Пример 2: SUM и AVG зарплат по отделам

SELECT dept_id,
       COUNT(*)        AS cnt,
       SUM(salary)     AS total_salary,
       AVG(salary)     AS avg_salary
FROM employees
WHERE dept_id IS NOT NULL
GROUP BY dept_id
ORDER BY total_salary DESC;

Результат:

+---------+-----+--------------+------------+
| dept_id | cnt | total_salary | avg_salary |
+---------+-----+--------------+------------+
|       5 |   1 |         8000 |       8000 |
|       3 |   1 |         7200 |       7200 |
|       1 |   1 |         6500 |       6500 |
|       4 |   1 |         5000 |       5000 |
+---------+-----+--------------+------------+

Пример 3: GROUP BY по нескольким столбцам

Можно группировать сразу по нескольким столбцам — строки объединяются только при совпадении всех значений:

-- Количество клиентов в каждом городе по каждому почтовому индексу
SELECT city, postal_code, COUNT(*) AS cnt
FROM customers
GROUP BY city, postal_code
ORDER BY city;

Результат:

+-----------+-------------+-----+
| city      | postal_code | cnt |
+-----------+-------------+-----+
| Berlin    | 12209       |   1 |
| Madrid    | 28001       |   1 |
| Madrid    | 28023       |   1 |
| Paris     | 75016       |   1 |
| Portland  | 97219       |   1 |
| Turin     | 10100       |   1 |
+-----------+-------------+-----+

Правила GROUP BY

Правило

Пояснение

В SELECT — только столбцы из GROUP BY или агрегаты

Нельзя выбрать столбец, по которому нет группировки

GROUP BY после FROM/WHERE

Порядок предложений строго фиксирован

NULL — отдельная группа

Все строки с NULL в столбце группировки образуют одну группу

Частая ошибка. Написать в SELECT столбец, которого нет в GROUP BY и который не обёрнут в агрегатную функцию. Большинство СУБД (кроме старых версий MySQL) сразу вернут ошибку.

Коротко

  • GROUP BY группирует строки с одинаковыми значениями, чтобы агрегаты считались по каждой группе.
  • В SELECT можно упоминать только столбцы из GROUP BY или агрегатные функции.
  • Идёт после FROM/WHERE, до HAVING/ORDER BY.
  • Для фильтрации групп используют HAVING — об этом следующая статья.
Проверьте себя
1. Для чего используется GROUP BY?
AДля сортировки строк по значению столбца
BДля фильтрации строк по условию
CДля группировки строк с одинаковыми значениями столбца и применения агрегатных функций к каждой группе
DДля объединения двух таблиц по общему полю
2. Запрос: SELECT dept_name, COUNT(emp_id) FROM departments LEFT JOIN employees ON ... GROUP BY dept_name. Что вернёт COUNT(emp_id) для отдела без сотрудников?
ANULL
B1
C0
DОшибку
3. Где в запросе должен стоять GROUP BY?
AПосле WHERE, но перед ORDER BY
BПеред FROM
CПосле ORDER BY
DПосле LIMIT
4. Какой запрос подсчитает количество сотрудников в каждом отделе, включая отделы без сотрудников?
ASELECT dept_name, COUNT(emp_id) FROM employees GROUP BY dept_name;
BSELECT t1.dept_name, COUNT(t2.emp_id) AS total FROM departments AS t1 LEFT JOIN employees AS t2 ON t1.dept_id = t2.dept_id GROUP BY t1.dept_name;
CSELECT dept_name, COUNT(*) FROM departments INNER JOIN employees ON departments.dept_id = employees.dept_id GROUP BY dept_name;
DSELECT dept_name, COUNT(dept_id) FROM departments GROUP BY dept_name;