Window functions

Running total (cumulative sum) in SQL

3 min · updated June 16, 2026

A running total is a window SUM ordered by date. The syntax is the same across all six dialects (you only need MySQL 8+ and SQLite 3.25+, where window functions arrived).

SELECT
  d,
  amount,
  SUM(amount) OVER (ORDER BY d
                    ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_total
FROM sales
ORDER BY d;

Per group — reset the total for each customer with PARTITION BY:

SELECT
  customer_id, d, amount,
  SUM(amount) OVER (PARTITION BY customer_id ORDER BY d
                    ROWS UNBOUNDED PRECEDING) AS running_total
FROM sales;

(ROWS UNBOUNDED PRECEDING is shorthand for ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW.)

The frame trap. If you write SUM(amount) OVER (ORDER BY d) with no frame, the default is RANGE UNBOUNDED PRECEDING — which sums all rows that tie on d together. Two sales on the same day both show the day-end total, not a true step-by-step total. Always add an explicit ROWS frame for a per-row running total. This applies in every dialect.

Works in MySQL 8+, PostgreSQL, SQLite 3.25+, BigQuery, Snowflake and DuckDB.

← All recipes