Shape / table · All operations
Compute a running total
A variant of Calculate across rows
Accumulate a sum, count, average, minimum or maximum from the first row of the partition down to the current row.
Status
- In arcform
Runs as a SQL step
arcform carries it as a
sql:step today.
The DuckDB step
Accumulate a sum, count, average, minimum and maximum from the first row to the current one
It reads the orders table the proofs share. Create it first:
CREATE TABLE orders AS SELECT * FROM (VALUES
(1, 'Alice Ng', 'north', DATE '2026-01-03', 120.00, 'widget', 'a|b', '{"channel":"web","items":[1,2]}'),
(2, 'alice ng', 'north', DATE '2026-01-05', 80.50, 'gadget', 'b', '{"channel":"shop","items":[3]}'),
(3, 'Bob Okafor', 'south', DATE '2026-01-05', 300.00, 'widget', 'a|c', '{"channel":"web","items":[4,5,6]}'),
(4, 'Carla Díaz', 'east', DATE '2026-01-09', NULL, 'gizmo', NULL, '{"channel":"web","items":[]}'),
(5, 'Bob Okafor', 'south', DATE '2026-01-12', 45.25, 'gadget', 'c', '{"channel":"phone","items":[7]}'),
(6, 'Dev Patel', 'west', DATE '2026-01-12', 510.00, 'widget', 'a', '{"channel":"web","items":[8,9]}'),
(7, 'Carla Diaz', 'east', DATE '2026-01-20', 99.99, 'gizmo', 'b|c', '{"channel":"shop","items":[10]}'),
(8, 'Dev Patel', 'west', DATE '2026-02-02', 510.00, 'widget', 'a', '{"channel":"web","items":[8,9]}'),
(9, 'Eve Larsen', NULL, DATE '2026-02-03', 15.00, 'gadget', NULL, '{"channel":"web","items":[11]}'),
(10, 'Alice Ng', 'north', DATE '2026-02-10', 220.00, 'gizmo', 'a|b|c', '{"channel":"phone","items":[12,13]}')
) t(order_id, customer, region, order_date, amount, product, tags, meta);Then run the statement:
CREATE TEMP TABLE result AS
SELECT product, order_id, amount,
sum(amount) OVER w AS running_sum,
count(amount) OVER w AS running_count,
round(avg(amount) OVER w, 4) AS running_avg,
min(amount) OVER w AS running_min,
max(amount) OVER w AS running_max
FROM orders
WINDOW w AS (PARTITION BY product ORDER BY order_date, order_id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW);
SELECT * FROM result ORDER BY product, order_id;
SELECT CASE WHEN ((SELECT list(running_sum ORDER BY order_id) FROM result WHERE product = 'widget') = [120.00, 420.00, 930.00, 1440.00] AND (SELECT list(running_count ORDER BY order_id) FROM result WHERE product = 'widget') = [1, 2, 3, 4] AND (SELECT list(running_avg ORDER BY order_id) FROM result WHERE product = 'gadget') = [80.5, 62.875, 46.9167] AND (SELECT list(running_min ORDER BY order_id DESC) FROM result WHERE product = 'gadget') = [15.00, 45.25, 80.50] AND (SELECT list(running_max ORDER BY order_id) FROM result WHERE product = 'widget') = [120.00, 300.00, 510.00, 510.00] AND (SELECT list(running_sum ORDER BY order_id) FROM result WHERE product = 'gizmo') IS NOT DISTINCT FROM [NULL, 99.99, 319.99] AND (SELECT list(running_count ORDER BY order_id) FROM result WHERE product = 'gizmo') = [0, 1, 2]) THEN 'as expected' ELSE error('the running sum, count, average, minimum and maximum from the first row of each product were not the expected values') END AS verdict;- It needs nothing installed beyond DuckDB itself.
- The statement checks its own result and raises an error when it is wrong: “the running sum, count, average, minimum and maximum from the first row of each product were not the expected values”.
- It passed on DuckDB
v2.0.0 (Cyanoptera) d54cbbe3df.
What other tools call it
Meridian's classification; an independent blind rating agreed on 95.7% of 324 rows (variant, version 3).
Alteryx Designer (1)
Running Totalread 2026-09-27Compute a cumulative sum on numeric fields, optionally per group
Bruin (1)
running_totalread 2026-09-27Running sum of a referenced metric over an ordered window
Duckle (1)
Cumulativeread 2026-09-25running sum, avg, count, min or max over an ordered window
EasyMorph (1)
Running totalread 2026-09-27Compute rank, cumulative count, running sum or share of total, table-wide or within groups
Polars (11)
Expr.cum_countread 2026-09-27Running count of the non-null values down the column
Expr.cum_maxread 2026-09-27Running maximum down the column
Expr.cum_minread 2026-09-27Running minimum down the column
Expr.cum_prodread 2026-09-27Running product down the column
Expr.cum_sumread 2026-09-27Running sum down the column
Expr.cumulative_evalread 2026-09-27Evaluate an expression over a window that grows by one row at each step
cum_countread 2026-09-27Running count of the non-null values of the named columns
cum_foldread 2026-09-27Running left fold across the columns of each row with a user function and a start value
cum_reduceread 2026-09-27Running left fold across the columns of each row with a user function, no start value
cum_sumread 2026-09-27Running sum of the named columns
cum_sum_horizontalread 2026-09-27Running sum across the columns of each row, returned as a struct
Tableau Prep (3)
RUNNING_AVGread 2026-09-27Running average from the partition's first row to the current row
RUNNING_SUMread 2026-09-27Running sum from the partition's first row to the current row
Running Calculationread 2026-09-27Cumulative sum or average of a value and the values before it
Background
A running total is the sum of a sequence of numbers, updated each time a new number is added to the sequence by adding it to the previous running total.[1] Another term for a running total is partial sum.[1] In PostgreSQL, sum(salary) OVER (ORDER BY salary) takes the sum from the first, lowest salary up through the current one.[2]
When to use it
A running total allows the total to be stated at any point in time without having to sum the entire sequence each time.[1] Reach for a running total for balances, year-to-date figures and anything else read as where the count stands so far. (Meridian's view)
In PostgreSQL, the running sum of sum(salary) OVER (ORDER BY salary) includes any duplicates of the current salary, so tied rows show the same total.[2] In DuckDB, the CURRENT ROW boundary means the current row and all its peers under RANGE or GROUPS framing, but only the current row under ROWS framing.[3] Write ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW, and order by a key with no ties, when each row needs its own step in the total. (Meridian's view)
In DuckDB, sum(column) OVER (ORDER BY time) gives a row with a NULL value the same cumulative value as the row that precedes it.[3] Where a missing amount should stop the total rather than be skipped over, fill or flag it before accumulating. (Meridian's view) Partition a running total by account, product or customer whenever each of them should keep a balance of its own. (Meridian's view)
The method
For the sequence (5, 8, 3, 2), whose total is 18, adding 6 to the running total of 18 gives the new running total of 24 without summing the sequence again.[1] Tabulating machines had dedicated registers that could accumulate running totals of numbers in preselected columns.[1]
Sources
- [1]Running total - Wikipediasecondary source
- [2]PostgreSQL: Documentation: 18: 3.5. Window Functionsdocumentation
- [3]Window Functions – DuckDBdocumentation