Running total
A windowed SUM keeps row-level detail while accumulating — the simplest window function to start with.
Syntax
SUM(expression) OVER (
[PARTITION BY partition_expression, ...]
ORDER BY sort_expression [ASC | DESC], ...
)| Parameter | Type | Notes |
|---|---|---|
| ORDER BY* | expression [ASC|DESC], … | Defines the accumulation order. With an |
| PARTITION BY | expression, … | Optional. Restarts the total per group instead of running across everything. |
What it does
SQL playground
Loading editor…
⌘/Ctrl + Enter
Each row keeps its own revenue, but running_total is the sum of every row
up to and including it. This is the core difference from GROUP BY, which
would collapse the months into a single summed row.
Per group
SQL playground
Loading editor…
⌘/Ctrl + Enter
PARTITION BY customer_id resets the accumulation at each customer boundary
instead of running across the whole table.
Gotchas
- This is the default frame, not a special case. Any
ORDER BYinsideOVER (…)without an explicitROWS/RANGEclause behaves this way — see window frames for what changes if you add one. - Ties in
ORDER BYare included together as one step under the defaultRANGEframe — two same-day orders both get the post-both total, not two different intermediate values. - Without
PARTITION BY, the total runs across the entire result set — easy to do by accident when you meant to reset it per group.
- Window frames — ROWS vs RANGE, and why the default frame behaves this way.
- Running totals & moving averages — moving averages, percent-of-total, and month-over-month change built on the same idea.
- LAG / LEAD — comparing to one specific prior row instead of accumulating all of them.
Safety check before shipping
- confirm the ordering is intentional and deterministic
- decide whether the total should reset per group (add PARTITION BY) or run across the full set
- check how ties in the ORDER BY column are meant to behave
Was this page helpful?