Глиф Учебники

Урок 5. Подсчёт и группировка — COUNT, SUM, AVG, GROUP BY, HAVING

Курс «SQL» · урок 5 из 10 · 4 минуты чтения

Часто нужна не сама таблица, а итог по ней: сколько товаров, на какую сумму, какая средняя цена. Для этого в SQL есть агрегатные функции — они превращают много строк в одно число. Запросы урока — к таблице products из третьего урока.

Итоги по всей таблице

SQL
SELECT COUNT(*)   AS products,
       SUM(qty)   AS items,
       MIN(price) AS min_price,
       MAX(price) AS max_price,
       AVG(price) AS avg_price
FROM products;
productsitemsmin_pricemax_priceavg_price
854028538.375
ФункцияЧто считает
COUNT(*)число строк
COUNT(столбец)число строк, где в столбце не NULL
COUNT(DISTINCT столбец)число разных значений
SUM(столбец)сумму
AVG(столбец)среднее
MIN(столбец), MAX(столбец)наименьшее и наибольшее (работает и с текстом, и с датами)

Аргументом может быть и выражение. Стоимость всего склада:

SQL
SELECT SUM(price * qty) AS stock_value FROM products;
stock_value
4280

Агрегатные функции работают и с WHERE — тогда считаются только отобранные строки: SELECT COUNT(*) FROM products WHERE category = 'Канцелярия'; вернёт 3.

Округление и деление

Среднее редко бывает круглым. ROUND(x, n) округляет до n знаков после запятой: ROUND(AVG(price), 1) даст 38.4.

Осторожно с делением. В SQLite и PostgreSQL целое, делённое на целое, — тоже целое, остаток отбрасывается:

SQL
SELECT 7 / 2 AS a, 7 / 2.0 AS b, 7 % 2 AS c;
abc
33.51

Поэтому SUM(price) / COUNT(*) для этой таблицы даст 38, а не 38.375. Чтобы деление было дробным, одно из чисел должно быть дробным: SUM(price) * 1.0 / COUNT(*). В MySQL 7 / 2 сразу даёт 3.5000, а целое деление там пишут 7 DIV 2.

Группы: GROUP BY

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

SQL
SELECT category, COUNT(*) AS products, SUM(qty) AS items
FROM products
GROUP BY category
ORDER BY category;
categoryproductsitems
Канцелярия3490
Сумки217
Техника233
Учёба10
GROUP BY category — 8 строк по 4 группам category products items Канцелярия Ручка 150 Тетрадь 300 Пенал 40 Канцелярия 3 490 Сумки Рюкзак 12 Сумка для ноутбука 5 Сумки 2 17 Техника Калькулятор 25 Наушники 8 Техника 2 33 Учёба Глобус 0 Учёба 1 0 в каждой группе отдельно: products = COUNT(*), items = SUM(qty)
GROUP BY category раскладывает восемь товаров по четырём категориям, COUNT(*) и SUM(qty) считаются в каждой группе отдельно, и в ответе по одной строке на группу.

Восемь строк превратились в четыре — по одной на категорию. Самый дешёвый и самый дорогой товар в каждой:

SQL
SELECT category, MIN(price) AS cheapest, MAX(price) AS dearest
FROM products
GROUP BY category
ORDER BY category;
categorycheapestdearest
Канцелярия212
Сумки7085
Техника3060
Учёба4545

Группировать можно и по нескольким столбцам: GROUP BY category, price — группа на каждую пару значений.

Что можно писать в SELECT с GROUP BY

В ответе одна строка на группу. Поэтому в SELECT допустимы только столбцы из GROUP BY и агрегатные функции. Запрос «категория и название товара» не имеет смысла: в группе «Канцелярия» три названия, какое из них показать?

SQL
SELECT category, name, COUNT(*) FROM products GROUP BY category;

PostgreSQL на такой запрос отвечает ошибкой column "products.name" must appear in the GROUP BY clause or be used in an aggregate function. MySQL в обычном режиме (ONLY_FULL_GROUP_BY, включён по умолчанию) — тоже ошибкой. SQLite запрос выполнит и подставит название из какой-то строки группы — ошибки нет только с виду. Не полагайтесь на это: такой запрос не перенесётся на другую СУБД. Как правильно найти, например, самый дорогой товар каждой категории, рассказано в уроке Подзапросы.

Отбор групп: HAVING

WHERE отбирает строки до группировки и ничего не знает об итогах. Для условий на итоги есть HAVING — он отбирает уже готовые группы:

SQL
SELECT category, COUNT(*) AS products
FROM products
GROUP BY category
HAVING COUNT(*) >= 2
ORDER BY category;
categoryproducts
Канцелярия3
Сумки2
Техника2

Учёба с одним товаром отсеялась. Попытка написать то же через WHERE COUNT(*) >= 2 закончится ошибкой: в SQLite — misuse of aggregate: COUNT(), в MySQL — Invalid use of group function.

WHERE и HAVING часто нужны вместе. Категории, где не меньше двух товаров дешевле 50 рублей:

SQL
SELECT category, COUNT(*) AS cheap
FROM products
WHERE price < 50
GROUP BY category
HAVING COUNT(*) >= 2;
categorycheap
Канцелярия3

Сначала WHERE оставил пять товаров дешевле 50, затем GROUP BY разложил их по категориям, затем HAVING оставил группы с двумя и более товарами. Подробнее о разнице — справка Чем WHERE отличается от HAVING.

1 WHERE price < 50 Ручка 2 Тетрадь 3 Пенал 12 Глобус 45 Калькулятор 30 Рюкзак 85 Наушники 60 Сумка для ноутбука 70 не прошли 2 GROUP BY category Канцелярия Ручка, Тетрадь, Пенал Учёба Глобус Техника Калькулятор 3 HAVING COUNT(*) >= 2 Канцелярия — 3 Учёба — 1 Техника — 1 в группе меньше двух строк category cheap Канцелярия 3 ответ
Сначала WHERE price < 50 отбирает пять товаров, GROUP BY раскладывает их по категориям, а HAVING COUNT(*) >= 2 оставляет только группу «Канцелярия» с тремя товарами.

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

Пишется запрос в одном порядке, а выполняется в другом:

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

Отсюда правило: в ORDER BY можно сослаться на псевдоним из SELECT, а в WHERE — нельзя (в PostgreSQL и MySQL это ошибка), ведь WHERE выполняется раньше. Две категории с самым дорогим складом:

SQL
SELECT category, SUM(price * qty) AS stock_value
FROM products
GROUP BY category
ORDER BY stock_value DESC
LIMIT 2;
categorystock_value
Канцелярия1680
Сумки1370

NULL в итогах

Агрегатные функции пропускают NULL: SUM и AVG считают только заполненные значения, COUNT(столбец) — только непустые строки. COUNT(*) считает все строки. Если в группе нет ни одного значения, SUM вернёт NULL, а не 0. Подробнее — в справках Как посчитать строки и NULL в SQL.

Коротко

Проверьте себя

1. Какой запрос посчитает товары в каждой категории?
2. Нужны категории со средней ценой выше 40. Где написать условие AVG(price) > 40?
3. Что вернёт SELECT 7 / 2; в SQLite?
4. Чем COUNT(*) отличается от COUNT(phone)?