Нарастающий итог SQL с SUM OVER
Нарастающий итог SQL показывает сумму от начала последовательности до текущей строки, не сворачивая исходные записи. Разберём накопительный итог SQL, кумулятивную сумму, PARTITION BY, ORDER BY и границы оконного фрейма.
Обновлено · Команда SQL Arena
Что потренируете
- SUM() OVER с сортировкой строк
- ROWS UNBOUNDED PRECEDING
- Разница между ROWS и RANGE
- PARTITION BY для независимых итогов
- Скользящее среднее по окну
Пример задачи в тренажёре
Так выглядит задача в SQL Arena: условие, редактор, результат и схема базы.
В таблице daily_sales находятся отдельные продажи магазинов. Для магазина с store_id = 1 выведите дату продажи в sale_date, сумму строки в amount и накопительную сумму в running_total. Если в один день было несколько продаж, они должны добавляться к итогу последовательно. Отсортируйте результат по дате и id продажи.
SELECT sale_date, amount, SUM(amount) OVER ( PARTITION BY store_id ORDER BY sale_date, id ROWS UNBOUNDED PRECEDING ) AS running_total FROM daily_sales WHERE store_id = 1 ORDER BY sale_date, id;
| # | sale_date | amount | running_total |
|---|---|---|---|
| 1 | 2026-10-01 | 120 | 120 |
| 2 | 2026-10-02 | 80 | 200 |
| 3 | 2026-10-02 | 50 | 250 |
| 4 | 2026-10-03 | 140 | 390 |
В таблице daily_sales находятся отдельные продажи магазинов. Для магазина с store_id = 1 выведите дату продажи в sale_date, сумму строки в amount и накопительную сумму в running_total. Если в один день было несколько продаж, они должны добавляться к итогу последовательно. Отсортируйте результат по дате и id продажи.
SELECT sale_date, amount, SUM(amount) OVER ( PARTITION BY store_id ORDER BY sale_date, id ROWS UNBOUNDED PRECEDING ) AS running_total FROM daily_sales WHERE store_id = 1 ORDER BY sale_date, id;
Как посчитать нарастающий итог SQL через SUM OVER
Запрос не группирует продажи по дням. Каждая строка daily_sales остаётся отдельной, поэтому две продажи 2 октября видны независимо. SUM(amount) превращается в оконную функцию благодаря OVER: агрегат больше не сокращает число строк, а вычисляет значение для каждой из них.
PARTITION BY store_id задаёт независимую последовательность для каждого магазина. В примере WHERE оставляет только store_id = 1, поэтому эффект PARTITION BY не так заметен, но в запросе сразу заложено правильное правило: если убрать фильтр, накопительный итог каждого магазина начнётся с нуля. Такой приём нужен для выручки по городам, балансов по счетам, накопленных очков игроков и других метрик внутри группы.
ORDER BY sale_date, id определяет точный порядок накопления. Одной даты недостаточно: 2 октября есть две строки. Дополнение id делает порядок однозначным. Фраза ROWS UNBOUNDED PRECEDING означает: взять все физические строки от начала окна до текущей включительно. Поэтому суммы идут 120, 200, 250, 390 и 450.
Нарастающий итог, накопительный итог и кумулятивная сумма в SQL обычно описывают один и тот же паттерн: для каждой строки считается сумма всех нужных значений до текущей позиции. SUM становится оконной функцией благодаря OVER, а ORDER BY задаёт последовательность накопления. PARTITION BY позволяет вести отдельный итог для каждой группы. Для предсказуемой построчной логики важно понимать оконный фрейм: ROWS UNBOUNDED PRECEDING означает строки от начала раздела до текущей строки. Если значения сортировки повторяются, дополнительный ключ в ORDER BY помогает определить стабильный порядок.
SELECT sale_date, amount, SUM(amount) OVER ( PARTITION BY store_id ORDER BY sale_date, id ROWS UNBOUNDED PRECEDING ) AS running_total FROM daily_sales WHERE store_id = 1 ORDER BY sale_date, id;
| sale_date | amount | running_total |
|---|---|---|
| 2026-10-01 | 120 | 120 |
| 2026-10-02 | 80 | 200 |
| 2026-10-02 | 50 | 250 |
| 2026-10-03 | 140 | 390 |
| 2026-10-04 | 60 | 450 |
Ошибки при расчёте накопительного итога SQL
Оставить только ORDER BY по дате
Если несколько строк имеют одинаковую дату, их внутренний порядок не определён. Для последовательного накопления лучше добавить стабильный ключ, например id. Иначе две одинаковые даты могут давать результат, который трудно интерпретировать и воспроизводить.
Не указать ROWS и получить поведение RANGE
В PostgreSQL окно с ORDER BY и без явной рамки по умолчанию связано с RANGE. Строки с одинаковым значением сортировки рассматриваются как peers, поэтому сумма может сразу включить все продажи одной даты. ROWS считает именно строки по очереди и подходит, когда нужен пошаговый итог.
Забыть PARTITION BY
Без разделения по магазину, пользователю, счёту или другой группе сумма продолжится через границу следующей сущности. Запрос синтаксически будет корректным, но бизнес-смысл результата изменится: вместо нескольких независимых итогов получится один общий.
Задачи по теме в тренажёре
Трек «Оконные функции от и до»После базовой накопительной суммы полезно потренироваться на задачах, где окно приходится проектировать под данные. В SQL Arena есть задачи на накопительное количество игровых событий, сумму продаж по городам и историю изменения товарных остатков. Более сложные сценарии соединяют оконные суммы с разбиением событий на сессии. Для последовательной практики по этой теме есть трек «Оконные функции от и до».
- MediumPremiumНакопительное количество сыгранных игрpostgresqlmysqlclickhouse
- HardPremiumНакопительная выручка водителейpostgresqlmysqlclickhouse
- ExpertPremiumИстория изменения остатка товара во времениpostgresqlclickhouse
- MediumPremiumНакопительная сумма продаж по городамpostgresqlclickhouse
- MediumPremiumСессионизация событий Учи.ру с границей 30 минутpostgresqlclickhouse
- MediumPremiumНумерация сессий в банковском приложенииpostgresql
- MediumPremiumДлительность игровых сессийpostgresql
- MediumPremiumСколько действий в визите личного кабинетаpostgresql
Справочник по теме
Подробные статьи с синтаксисом и примерами в справочнике SQL Arena.
Частые вопросы
Как посчитать нарастающий итог SQL?
Обычно используют SUM(column) OVER с ORDER BY, определяющим порядок накопления. Для явного построчного окна можно задать ROWS UNBOUNDED PRECEDING.
Что такое накопительный итог SQL?
Это значение, которое последовательно накапливает сумму от начала выбранной последовательности до текущей строки. В отличие от GROUP BY, оконная функция позволяет сохранить исходные строки.
Как считается кумулятивная сумма SQL?
Кумулятивную сумму удобно считать через SUM() OVER (ORDER BY ...). Если итог должен начинаться заново для каждой группы, в окно добавляют PARTITION BY.
Зачем ROWS UNBOUNDED PRECEDING в нарастающем итоге?
Такой фрейм задаёт диапазон от первой строки раздела до текущей строки. Это явно описывает построчное накопление и помогает избежать неожиданной обработки строк с одинаковыми значениями сортировки.
Практика нарастающего итога в SQL
В SQL Arena можно решать задачи с накопительными суммами, скользящими окнами, ранжированием и сессиями. У каждой задачи свои таблицы, решение проверяется на скрытых данных. В PostgreSQL, MySQL, ClickHouse и BigQuery работают отдельные песочницы прямо в браузере. Бесплатно доступны 292 задачи без привязки карты, а для системной практики по этой теме можно перейти в трек «Оконные функции от и до».
Решать задачи с оконными функциями