Python: уроки, справочник и задачи

GROUP BY в SQL: группировка и агрегирование данных

Раздел: SQL и SQLite · Обновлено 31.10.2026

Оператор GROUP BY в SQL объединяет строки с одинаковыми значениями в группы, чтобы посчитать по каждой из них сумму, количество, среднее или максимум. В статье на одной учебной таблице разобраны группировка по одному и нескольким столбцам, фильтрация групп через HAVING, порядок выполнения запроса, типичные ошибки и запуск таких запросов из Python.

Оператор GROUP BY
Фото: Anonimski / CC0, Wikimedia Commons
Содержание статьи
  1. Учебная таблица для примеров
  2. Синтаксис и принцип работы
    1. Правило для столбцов в SELECT
  3. Группировка по нескольким столбцам
    1. Как GROUP BY обрабатывает NULL
  4. HAVING и WHERE: фильтрация до и после группировки
  5. Порядок выполнения запроса
  6. Группировка вместе с JOIN и group_concat
  7. Частые вопросы
    1. Можно ли использовать GROUP BY без агрегатных функций?
    2. Откуда ошибка misuse of aggregate при условии в WHERE?
    3. Как отсортировать группы по сумме?
    4. Как посчитать количество групп?

Учебная таблица для примеров

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

import sqlite3

conn = sqlite3.connect(':memory:')
conn.executescript('''
CREATE TABLE sales (id INTEGER PRIMARY KEY, manager TEXT,
                    city TEXT, amount INTEGER, sold_on TEXT);
INSERT INTO sales (manager, city, amount, sold_on) VALUES
  ('Иванов',  'Москва', 1200, '2026-09-03'),
  ('Петрова', 'Казань',  800, '2026-09-05'),
  ('Иванов',  'Москва',  450, '2026-09-12'),
  ('Сидоров', 'Москва', 2000, '2026-10-01'),
  ('Петрова', 'Казань', 1500, '2026-10-07'),
  ('Иванов',  'Тверь',   300, '2026-10-15'),
  ('Сидоров', NULL,      700, '2026-10-20');
''')

Скопируйте этот код и выполняйте запросы из статьи через conn.execute(sql).fetchall(), чтобы видеть результат. Тот же набор данных можно создать в DB Browser for SQLite и запускать запросы там.

Синтаксис и принцип работы

GROUP BY ставится после FROM и WHERE. В нем перечисляют столбцы, по значениям которых строки собираются в группы. Для каждой группы запрос возвращает ровно одну строку.

sql = '''
SELECT manager, COUNT(*) AS deals, SUM(amount) AS total
FROM sales
GROUP BY manager
'''
for row in conn.execute(sql):
    print(row)
# ('Иванов', 3, 1950)
# ('Петрова', 2, 2300)
# ('Сидоров', 2, 2700)

Семь строк таблицы превратились в три: по одной на каждого менеджера. COUNT(*) посчитал сделки внутри группы, SUM(amount) сложил суммы. Без агрегатной функции GROUP BY просто вернул бы уникальные значения столбца, как SELECT DISTINCT. Полный список функций, которые работают с группами (SUM, AVG, MIN, MAX, COUNT), описан в статье об агрегатных функциях SQL.

Правило для столбцов в SELECT

В списке SELECT при группировке могут стоять только две категории выражений: столбцы, перечисленные в GROUP BY, и агрегатные функции. Если добавить в запрос выше столбец city, у Иванова окажется два города, и непонятно, какой из них показывать.

PostgreSQL, SQL Server и MySQL в строгом режиме на такой запрос выдадут ошибку. SQLite запрос выполнит и подставит значение из произвольной строки группы. Это не ошибка, а особенность SQLite, но полагаться на такой результат нельзя. Исключение одно: если в запросе есть ровно один MAX() или MIN(), SQLite возьмет остальные столбцы из той строки, где найден максимум или минимум.

sql = 'SELECT manager, MAX(amount), sold_on FROM sales GROUP BY manager'
print(conn.execute(sql).fetchall())
# [('Иванов', 1200, '2026-09-03'), ('Петрова', 1500, '2026-10-07'),
#  ('Сидоров', 2000, '2026-10-01')]

Группировка по нескольким столбцам

Если указать в GROUP BY несколько столбцов, группой станет каждая уникальная комбинация их значений. Так можно посмотреть продажи менеджеров с разбивкой по городам.

sql = '''
SELECT manager, city, SUM(amount)
FROM sales
GROUP BY manager, city
'''
# ('Иванов', 'Москва', 1650)
# ('Иванов', 'Тверь', 300)
# ('Петрова', 'Казань', 2300)
# ('Сидоров', None, 700)
# ('Сидоров', 'Москва', 2000)

Порядок столбцов в GROUP BY на состав групп не влияет. Группировать можно и по выражению: например, функция strftime('%Y-%m', sold_on) превращает дату в месяц, и запрос SELECT strftime('%Y-%m', sold_on) AS month, SUM(amount) FROM sales GROUP BY month вернет выручку по месяцам: 2450 за сентябрь и 4500 за октябрь. SQLite разрешает ссылаться в GROUP BY на псевдоним из SELECT, но не все СУБД так умеют.

Как GROUP BY обрабатывает NULL

Все строки с NULL в столбце группировки попадают в одну общую группу. Запрос SELECT city, COUNT(*) FROM sales GROUP BY city вернет четыре строки, и первой окажется (None, 1). При этом COUNT(city) не считает NULL, а COUNT(*) считает все строки: на нашей таблице это 6 и 7.

HAVING и WHERE: фильтрация до и после группировки

WHERE отбирает отдельные строки до того, как они будут сгруппированы, и не может использовать агрегатные функции. HAVING применяется к уже готовым группам и как раз проверяет их итоги. Найдем менеджеров, у которых общая сумма продаж больше 2000:

sql = '''
SELECT manager, SUM(amount) AS total
FROM sales
GROUP BY manager
HAVING SUM(amount) > 2000
'''
# [('Петрова', 2300), ('Сидоров', 2700)]

Оба условия можно сочетать в одном запросе. Ниже WHERE оставляет только октябрьские сделки, а HAVING - менеджеров, у которых таких сделок не меньше двух:

sql = '''
SELECT manager, SUM(amount)
FROM sales
WHERE sold_on >= '2026-10-01'
GROUP BY manager
HAVING COUNT(*) >= 2
'''
# [('Сидоров', 2700)]

Условия, которые не зависят от агрегатов, всегда пишите в WHERE: база отбросит лишние строки раньше, и группировать придется меньше данных. Подробнее про фильтрацию строк рассказано в статье об операторе WHERE.

ПризнакWHEREHAVING
Когда срабатываетДо группировкиПосле группировки
Что фильтруетОтдельные строкиГруппы
Агрегатные функцииНельзяМожно
Работает без GROUP BYДаДа, вся таблица считается одной группой

Порядок выполнения запроса

Части SELECT-запроса пишутся в одном порядке, а логически выполняются в другом. Понимание этой последовательности объясняет большинство ошибок с группировкой.

  1. FROM и JOIN - собираются исходные строки;
  2. WHERE - отбрасываются строки, не прошедшие условие;
  3. GROUP BY - строки собираются в группы;
  4. HAVING - отбрасываются группы;
  5. SELECT - вычисляются итоговые столбцы и псевдонимы;
  6. ORDER BY - результат сортируется;
  7. LIMIT - отбирается нужное количество строк.

Отсюда следует, что в WHERE нельзя использовать агрегаты: группы на этом шаге еще не существуют. А сортировать итог по сумме можно, потому что ORDER BY выполняется последним: ORDER BY total DESC выведет лучших менеджеров первыми. Варианты сортировки описаны в статье про ORDER BY.

Группировка вместе с JOIN и group_concat

На практике группируют часто не одну таблицу, а результат соединения. Например, если менеджеры хранятся в отдельной таблице с отделами, запрос сначала соединяет таблицы через JOIN, а потом группирует по названию отдела. О типах соединений подробно рассказано в материале об операторе JOIN.

Еще одна полезная функция SQLite для группировки - group_concat(). Она склеивает значения группы в одну строку через запятую или заданный разделитель.

sql = '''
SELECT manager, group_concat(DISTINCT city)
FROM sales
GROUP BY manager
'''
# ('Иванов', 'Москва,Тверь')
# ('Петрова', 'Казань')
# ('Сидоров', 'Москва')

Для Сидорова город NULL в результат не попал: group_concat, как и остальные агрегаты, пропускает пустые значения. Точная грамматика SELECT в SQLite приведена в документации SQLite.

Частые вопросы

Ответы на вопросы, которые чаще всего возникают при группировке данных.

Можно ли использовать GROUP BY без агрегатных функций?

Можно, тогда запрос вернет уникальные комбинации значений, как SELECT DISTINCT. Но для этой задачи DISTINCT читается понятнее.

Откуда ошибка misuse of aggregate при условии в WHERE?

WHERE выполняется до группировки, и итогов по группам еще нет. Перенесите условие с SUM, COUNT или AVG в HAVING.

Как отсортировать группы по сумме?

Добавьте в конец запроса ORDER BY SUM(amount) DESC или сортируйте по псевдониму: ORDER BY total DESC.

Как посчитать количество групп?

Оберните запрос с группировкой в подзапрос: SELECT COUNT(*) FROM (SELECT manager FROM sales GROUP BY manager). Для одного столбца проще написать COUNT(DISTINCT manager).