datarekha
SQL Medium Asked at AmazonAsked at StripeAsked at Airbnb

How do you compute a running total (cumulative sum) using a window function, and what frame clause does it use by default?

The short answer

Use SUM() OVER (ORDER BY ...) — the default frame is RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW. This works correctly for strictly increasing order columns, but silently over-counts when multiple rows share the same ORDER BY value because the RANGE default includes all peers.

How to think about it

A running total is the gateway window function — interviewers use it to check that you understand the frame clause, not just the syntax. The follow-up is always “what happens when two rows share the same ORDER BY value?”, so address that head-on.

The pattern is SUM(amount) OVER (ORDER BY ...) with an explicit ROWS frame, and PARTITION BY to restart the total per group. The explicit frame is what gives you the classic staircase, processing each physical row exactly once.

A worked example — partitioned running total

Reset the cumulative sum per customer; customer 1 has two orders on the same date:

SELECT customer_id, order_date, amount,
       SUM(amount) OVER (
         PARTITION BY customer_id
         ORDER BY order_date
         ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
       ) AS running_total_rows
FROM orders
ORDER BY customer_id, order_date, id;
customer_idorder_dateamountrunning_total_rows
12024-01-01100100
12024-01-0250150
12024-01-0280230
22024-01-01200200
22024-01-03120320

Two things to notice. The total resets at customer 2 (back to 200, not continuing from 320) — that’s PARTITION BY doing its job. And customer 1’s two Jan-02 orders staircase cleanly to 150 then 230, because of the explicit ROWS frame. Drop that frame and the default RANGE would give both Jan-02 rows the same 230 — the tell-tale “stalled” running total.

The frame options at a glance

Frame keywordMeaning
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROWpartition start up to this physical row (running total)
ROWS BETWEEN N PRECEDING AND CURRENT ROWcurrent row plus N before it (moving window)
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROWup to and including all peers on the ORDER BY value

A moving average is the same machine with a bounded frame: AVG(amount) OVER (ORDER BY order_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) gives a 7-row look-back (6 before + current), and early rows simply use however many rows exist.

Learn it properly Window functions

Keep practising

All SQL questions

Explore further

Skip to content