Урок курса
Агрегатные функции: COUNT, SUM, AVG, MIN, MAX
SQL с нуля: бесплатный курс с практикойВ прошлом уроке функции вроде ROUND или LENGTH принимали одно значение и возвращали одно значение — каждая строка обрабатывалась независимо. Агрегатные функции работают иначе: они принимают сразу весь набор строк и сворачивают его в одно итоговое число.
Агрегатная функция: набор строк → одно итоговое значение
Представьте, что вам нужно узнать не содержимое конкретной строки, а что-то про всю таблицу целиком: сколько в ней заказов, какова их общая сумма, каков средний чек. Обычный SELECT здесь не поможет — он возвращает по одной строке на каждую запись. Агрегатная функция делает другое: она получает на вход множество строк и возвращает одно число.
Когда в SELECT стоят только агрегатные функции и нет GROUP BY, результат — всегда ровно одна строка. Не одна строка на каждый заказ, не пустой результат — именно одна строка с итоговыми значениями по всей таблице.
Пять основных функций выглядят так:
SELECT
COUNT(*) AS количество,
SUM(amount) AS итого,
AVG(amount) AS среднее,
MIN(amount) AS минимум,
MAX(amount) AS максимум
FROM orders;
Этот запрос вернёт одну строку с пятью столбцами. COUNT(*) — сколько строк в таблице. SUM(amount) — арифметическая сумма всех значений столбца amount. AVG(amount) — среднее арифметическое. MIN и MAX — наименьшее и наибольшее значение соответственно.
Почему именно одна строка? Потому что агрегатные функции в этом запросе описывают весь набор как единое целое. Пока мы считаем итог по всей таблице, в SELECT оставляйте только агрегатные выражения: тогда результатом будет одна строка. Как получить отдельный итог для каждой категории или клиента, разберём в следующем уроке.
COUNT(*) vs COUNT(столбец) и поведение NULL в агрегатных функциях
Разберём тонкость, которая часто даёт неожиданные результаты. Пусть таблица orders содержит пять строк, но в двух из них amount равен NULL — например, заказы зарегистрированы, но сумма ещё не проставлена.
SELECT
COUNT(*) AS все_строки,
COUNT(amount) AS строки_с_суммой,
SUM(amount) AS итого
FROM orders;
Результат:
| все_строки | строки_с_суммой | итого |
|---|---|---|
| 5 | 3 | … |
COUNT(*) считает строки — все, независимо от того, что в них записано. COUNT(amount) считает только те строки, где amount не равен NULL. Поэтому первое число — 5, второе — 3.
SUM, AVG, MIN, MAX ведут себя как COUNT(столбец): они просто игнорируют строки с NULL и работают только с теми значениями, которые есть. SUM(amount) сложит три ненулевых значения и вернёт их сумму. AVG поделит ту же сумму на 3, а не на 5 — это важно: среднее считается по фактически присутствующим значениям, а не по общему количеству строк.
Есть крайний случай: если NULL стоит в столбце у всех строк, SUM, AVG, MIN, MAX вернут NULL. Это логично — нечего агрегировать. А вот COUNT(*) в той же ситуации вернёт не NULL, а количество строк — он вообще никогда не возвращает NULL, даже для полностью пустой таблицы вернёт 0.
Практическое следствие: если вы хотите знать, сколько заказов у вас в системе — используйте COUNT(*). Если хотите знать, для скольких из них уже проставлена сумма — используйте COUNT(amount). Это разные вопросы, и разница проявляется именно там, где есть NULL.
Агрегация после WHERE: функция видит только отфильтрованные строки
Когда в запросе есть WHERE, агрегатная функция получает на вход не всю таблицу, а только строки, прошедшие фильтр. Порядок выполнения жёсткий: сначала FROM определяет таблицу, затем WHERE отбирает строки, и лишь потом агрегатная функция работает с тем, что осталось.
SELECT
COUNT(*) AS количество,
SUM(amount) AS итого
FROM orders
WHERE amount >= 1000;
Этот запрос считает и суммирует только те заказы, у которых amount не меньше 1000. Заказы с меньшей суммой до агрегатной функции не доходят вообще — они отсеяны раньше.
Важно понять, что WHERE и агрегация — это не конкуренты, а последовательные этапы. WHERE не знает об агрегатных функциях, а агрегатная функция не знает о строках, которые WHERE уже отбросил. Именно поэтому нельзя написать WHERE COUNT(*) > 10 — в момент проверки WHERE агрегация ещё не произошла, значения COUNT(*) просто не существует. Для фильтрации по результату агрегации есть отдельный инструмент, который появится в следующих уроках.
Практически это означает: комбинируя WHERE с агрегатными функциями, вы задаёте вопрос «какова итоговая картина по конкретному подмножеству данных?». Например, средний чек среди заказов дороже 1000, количество заказов за конкретный период или максимальная сумма среди заказов дешевле 5000 — всё это пишется именно так: условие в WHERE, итог в SELECT.
Попробуйте решить
Таблица orders содержит пять строк. Сколько строк вернёт запрос без GROUP BY?
SELECT COUNT(*), SUM(amount), MIN(amount), MAX(amount) FROM orders;
Продолжить с проверкой и прогрессом
Откройте интерактивный раннер с заданиями урока.
