Create a Cumulative Sum Column in MySQL
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
A cumulative sum column shows a running total as rows progress in a defined order. In modern MySQL, the cleanest solution is a window function with SUM() OVER (...); in older versions, you can simulate the same behavior with user-defined variables, but that approach is less robust and more version-sensitive.
Core Sections
Use a window function in MySQL 8+
If you are on MySQL 8 or later, use SUM() as a window function. The crucial part is the ORDER BY, because a cumulative total only makes sense relative to a row order.
This returns each row together with the total of amount from the beginning of the ordered result set up to the current row.
Including a tie-breaker such as id is often important. If multiple rows share the same timestamp and you do not break ties explicitly, the running order may be ambiguous.
Partition when totals should reset per group
Often the running total should restart for each customer, account, or category. In that case, use PARTITION BY.
This keeps one cumulative total per customer rather than one global total.
Understand the frame behavior
MySQL window functions can use different frames. For running totals, the usual intent is “from the first row in the partition up to the current row.” MySQL’s default with an ordered window often aligns with that intent, but being explicit can make the query easier to read.
That wording makes the running-total behavior obvious to future readers.
Older MySQL versions: variables as a fallback
Before MySQL 8, a common workaround used session variables.
This can work, but it is more brittle. Query evaluation order and optimization details can make variable-based solutions harder to reason about than window functions. Use the window-function version whenever the server supports it.
Make sure the ordering column is meaningful
A cumulative sum without a stable order is not well-defined. If you order by a non-unique column such as created_at alone, tied rows can produce surprising totals depending on execution details.
The safe pattern is to order by:
- the business column that defines progression
- plus a unique tie-breaker column
That makes the running total deterministic. It also keeps review and troubleshooting simpler, because the cumulative progression no longer depends on implicit engine choices for tied rows.
Common Pitfalls
- Forgetting
ORDER BYin the window definition, which turns the query into a plain partition total rather than a cumulative total. - Using a non-deterministic ordering column and getting unstable running totals when multiple rows tie.
- Reaching for user-defined variables in MySQL 8+ when a clearer window-function solution is available.
- Confusing “running total per group” with “running total across the whole table” and omitting
PARTITION BYwhen it is needed. - Assuming the displayed outer
ORDER BYalone controls the cumulative logic when the real ordering for the running sum is inside the window clause.
Summary
- In MySQL 8+, use
SUM(amount) OVER (ORDER BY ...)for cumulative totals. - Add
PARTITION BYwhen the running total should restart for each group. - Use a stable ordering with a tie-breaker column so results stay deterministic.
- User-defined variables are a fallback for older MySQL versions, not the preferred modern solution.
- The window clause defines the cumulative logic, so order it carefully.

