How to Calculate a Running Total (Cumulative Sum) in SQL
Compute cumulative running totals over time or partitioned groups using SUM() OVER (ORDER BY ...).
SELECT
order_date,
amount,
SUM(amount) OVER (ORDER BY order_date ASC) AS running_total
FROM orders;Step-by-Step Query Breakdown
SELECT
order_date,
amount,
SUM(amount) OVER (ORDER BY order_date ASC) AS running_total
FROM orders;Calculates the cumulative sum of amount ordered by date from the first row up to the current row.
SELECT
customer_id,
order_date,
amount,
SUM(amount) OVER (
PARTITION BY customer_id
ORDER BY order_date ASC
) AS customer_cumulative_spend
FROM orders;Resets the running total back to 0 whenever the customer_id changes.
WITH monthly_sales AS (
SELECT DATE_TRUNC('month', order_date) AS month, SUM(amount) AS monthly_revenue
FROM orders
GROUP BY 1
)
SELECT
month,
monthly_revenue,
SUM(monthly_revenue) OVER (ORDER BY month ASC) AS cumulative_annual_revenue
FROM monthly_sales;Aggregates monthly sums first, then calculates the cumulative running revenue.
Critical SQL Pitfalls & Precautions
- β’If multiple rows have the exact same ORDER BY value, SUM() OVER will calculate the sum including all peer rows with the same value unless you include an ID tie-breaker (ORDER BY date, id).
Running Total (Window Function) - Frequently Asked Questions
Common questions about transactional safety, indexing, and engine compatibility.
Yes! SQLite added full window function support in version 3.25.0+.
Related SQL Query Guides
Browse All SQL RecipesIdentify rows with duplicate column values using GROUP BY and HAVING count(*) > 1, and safely remove duplicates using ROW_NUMBER() CTEs.
Compare row-by-row logging (DELETE), fast table emptying with auto-increment reset (TRUNCATE), and complete table removal (DROP).
Atomically insert a new row or update an existing row if a unique constraint or primary key conflict is encountered.
Build a production database index on a table with millions of rows without locking out live INSERT, UPDATE, or DELETE operations.
Update values in one table based on matching columns or aggregations in a secondary table.
Reset the next auto-generated primary key ID back to 1 or synchronize it with the maximum existing ID in the table.