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:

SQL
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:

SQL
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-27

    Compute a cumulative sum on numeric fields, optionally per group

Bruin (1)
  • running_totalread 2026-09-27

    Running sum of a referenced metric over an ordered window

Duckle (1)
  • Cumulativeread 2026-09-25

    running sum, avg, count, min or max over an ordered window

EasyMorph (1)
  • Running totalread 2026-09-27

    Compute rank, cumulative count, running sum or share of total, table-wide or within groups

Polars (11)
  • Expr.cum_countread 2026-09-27

    Running count of the non-null values down the column

  • Expr.cum_maxread 2026-09-27

    Running maximum down the column

  • Expr.cum_minread 2026-09-27

    Running minimum down the column

  • Expr.cum_prodread 2026-09-27

    Running product down the column

  • Expr.cum_sumread 2026-09-27

    Running sum down the column

  • Expr.cumulative_evalread 2026-09-27

    Evaluate an expression over a window that grows by one row at each step

  • cum_countread 2026-09-27

    Running count of the non-null values of the named columns

  • cum_foldread 2026-09-27

    Running left fold across the columns of each row with a user function and a start value

  • cum_reduceread 2026-09-27

    Running left fold across the columns of each row with a user function, no start value

  • cum_sumread 2026-09-27

    Running sum of the named columns

  • cum_sum_horizontalread 2026-09-27

    Running sum across the columns of each row, returned as a struct

Tableau Prep (3)
  • RUNNING_AVGread 2026-09-27

    Running average from the partition's first row to the current row

  • RUNNING_SUMread 2026-09-27

    Running sum from the partition's first row to the current row

  • Running Calculationread 2026-09-27

    Cumulative 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]