A running total is carried down each group's rows under a chosen ordering — how does it differ from the group's total?
answer
- one value per row, not per group
- resets at every group boundary
- the last row equals the group's total
- the first group always looks right
basics
~20 sA running total is one value per row — that row plus every earlier row of the same group under the chosen ordering. The group's total is one value for the whole group, and it is what the group's last row already holds.
solid answer
~50 sThe group's total is a single number per group; a running total is a number on every row, and the two meet only at the end. Under the ordering you chose, each row's value is the sum of its own amount and every earlier row of its group, so the first row holds its own amount and the last row holds the group's total. Three properties follow. The result has the same row count as the input. Every value except the last depends on the ordering, while the last does not, because addition does not care about order. And the carry must reset at each group boundary — if the accumulation is applied to the whole column rather than inside the grouped operation, it runs straight through the boundary and every group after the first is inflated by everything before it. That last bug is silent: the first group is always right.
code
pseudocode · 12 linesrunning = 0
previous_key = none
for row in rows, ordered by grouping key then by the chosen ordering:
if row.grouping_key != previous_key:
running = 0 # the reset a whole-column carry never performs
previous_key = row.grouping_key
running = running + row.amount
row.running_total = running
# first row of a group -> that row's own amount
# last row of a group -> that group's totalgo deeper
Hold the shape: one value on every row, climbing through the group, starting again at the next group. The last row of a group carries that group's total.
Explain the reset and what happens without it — the first group right, every later group offset by everything before it — and why the intermediate values depend on the ordering while the final one does not.
Show the checks you run before trusting someone else's carry: last row against an independently computed group total, first row against its own amount, row count unchanged, and the carry dropping at boundaries.
The angle is what the team standardises: whether carries are always expressed inside the grouped operation, and what reconciliation ships alongside any per-row cumulative column that a report depends on.
## Three totals, three row counts The word *total* covers three different objects in this area, and the distinguishing fact is how many numbers each one is: | Object | How many values | What it is | |---|---|---| | the group's total | one per group | every row of the group added together | | a running total carried down the group's rows | one per row | this row plus every earlier row of the same group, under the chosen ordering | | the total across all groups | one, for the whole table | every row added together, regardless of key | A running total is the middle one. It belongs to the same family as numbering rows inside a group: a grouped operation — one pass that keys every row, computes per key and reassembles — whose answer is a value for each input row rather than a summary. The result has exactly as many rows as the input. ## What the ordering does to it The carry only means something under an ordering, because *earlier* only means something under an ordering. Order a customer's payments by date and each row shows the balance paid so far; order the same rows by amount and each row shows something with no business meaning at all, even though every number in the column is arithmetically correct. Two properties are worth holding on to: - **Every value except the last depends on the ordering.** Change the ordering and the intermediate numbers all move. - **The last row's value does not.** For a sum — and likewise for a running maximum or a running product — reordering the same set of values cannot change the result of folding all of them, so the final row always holds the group's total. That is a useful reconciliation: the last row of every group should equal the one-row-per-group total computed independently. It is not a general property of every carry, only of the order-insensitive ones. ## The reset, and the bug that skips it The carry must start again at every group boundary. That is the whole difference between a running total *inside* a group and a running total down the column. ``` running = 0 previous_key = none for row in rows, ordered by grouping key then by the chosen ordering: if row.grouping_key != previous_key: running = 0 previous_key = row.grouping_key running = running + row.amount row.running_total = running ``` Remove the three lines of the guard and you have the classic defect: the accumulation applied to the whole column instead of inside the grouped operation. The symptoms are specific and worth recognising: 1. **The first group is correct.** Nothing has accumulated yet, so the eye that spot-checks the top of the output sees nothing wrong. 2. **Every later group is offset** by the sum of everything before it — a constant shift per group, not a random error. 3. **The last value of the table** equals the total across all groups, not the last group's total. 4. **The column never decreases**, even where a group's amounts should have restarted from a small number. That last one is the cheapest check. Plot or scan the carry per group: inside a group of non-negative amounts it should climb from a small number, and at the boundary it should drop back down. A column that only climbs, end to end, never reset. ## Where designs differ Two forks show up when you read someone else's result: - **Whether the accumulation is grouped at all.** In some designs the accumulation is applied to a handle that already carries the split, and the reset is implicit; in others it is an operation on a column that knows nothing about groups, and the split must be supplied. The same English phrase — *a running total by customer* — describes both, and only one of them resets on its own. - **What order the result comes back in.** Some designs align the carry back onto the rows where they already sat, leaving the table's arrangement untouched; others hand back the rows arranged by the ordering the carry used. Neither is wrong, but a reader who assumes the wrong one misreads the output. ## What to check before trusting one - The last row of a sampled group equals that group's total computed separately. - The first row of every group equals that row's own amount. - The result's row count equals the input's. - The carry drops at group boundaries rather than climbing through them. Those four take a minute and catch every version of this that matters: a missing reset, an ordering nobody meant, and a carry that was quietly collapsed into a per-group summary somewhere along the way.
- Why is a missing reset so easy to miss in review?Because the first group is always right and the error is a constant offset rather than a wrong-looking number. The values stay plausible, the row count is unchanged, and the column is still monotonic — so nothing a quick scan looks at is obviously broken.
- The last row of a group does not match the group's total computed separately. What does that tell you?That the two saw different rows. Either the carry ran over a different set — a reset in the wrong place, or rows excluded before one step and not the other — or the two were computed against different groupings. It is not an ordering problem, since a total is order-insensitive.
saying these in an interview costs you the question
- Confuses the running total with the group's single total.
- Assumes an accumulation over the whole column resets itself at each group boundary.
- Thinks the intermediate values do not depend on the chosen ordering.
- Believes the last row's value differs from the group's total.
- Reports one number per group when the result carries one per row.