Recent chats
ASKGEEZ Q&A GUIDETechnology & devices

How can I calculate a running total with a window function?

Quick answer

Use SUM(amount) OVER (...) with an ORDER BY inside the window and a frame from the first row through the current row. For example, ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW includes expenses up to that point in the chosen sequence. Add a stable tie-breaker if dates repeat, and use the query’s outer ORDER BY to control display order. [S1]

LET’S WORK THROUGH IT

The question, the details, the next step.

Question

I have an expenses table and want each row to show the total spent up to that date. How do I write that in SQLite?

Geez

Assuming your table is named expenses and has expense_id, expense_date, and amount columns, use SUM as a window function:

SELECT
  expense_id,
  expense_date,
  amount,
  SUM(amount) OVER (
    ORDER BY expense_date, expense_id
    ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
  ) AS running_total
FROM expenses
ORDER BY expense_date, expense_id;

The OVER clause turns the aggregate into a window calculation. Its ORDER BY sets the sequence used to decide which expenses come before each row; the explicit frame includes rows from the start of that sequence through the current row. [S1] Here, expense_id is a tie-breaker so expenses on the same date have a consistent sequence, assuming it uniquely distinguishes rows.

Follow-up question

Could you show what the query returns with some sample expenses?

Geez

Here is a made-up example, assuming these rows are in expenses:

expense_id | expense_date | amount
1          | 2025-04-02   | 12.00
2          | 2025-04-03   | 8.50
3          | 2025-04-03   | 4.00
4          | 2025-04-06   | 10.00

Using the query above, the result would be:

expense_id | expense_date | amount | running_total
1          | 2025-04-02   | 12.00  | 12.00
2          | 2025-04-03   | 8.50   | 20.50
3          | 2025-04-03   | 4.00   | 24.50
4          | 2025-04-06   | 10.00  | 34.50

The two expenses dated April 3 are accumulated in expense_id order because that is the tie-breaker specified in the window. That ordering is a choice for this example; if you have a meaningful within-day sequence, use that column instead.

Follow-up question

Why do I need both ORDER BY clauses? And what does the frame specify?

Geez

The ORDER BY inside OVER defines the calculation sequence. The final ORDER BY after FROM controls the order in which rows are displayed. They serve different purposes, so keep the outer one when you want the results shown in date-and-ID order. [S1]

The explicit frame, ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW, starts at the first row in the ordered sequence and ends at the current row. That makes each result accumulate from the beginning through that row. [S1] The supplied SQLite guidance illustrates this frame; if you omit it or use another database, consult that database’s documentation rather than assuming identical behavior.

Follow-up question

What if multiple expenses have the same date, and can I filter on the running total?

Geez

When dates repeat, add a stable tie-breaker—such as an expense ID—to the window’s ORDER BY. That defines which tied row comes first in the calculation. Use the same ordering in the outer ORDER BY if you want the displayed rows to follow that sequence. [S1]

To filter on the calculated total in SQLite, put the window calculation in a subquery and apply the condition outside it:

SELECT *
FROM (
  SELECT
    expense_id,
    expense_date,
    amount,
    SUM(amount) OVER (
      ORDER BY expense_date, expense_id
      ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
    ) AS running_total
  FROM expenses
) AS totals
WHERE running_total >= 25
ORDER BY expense_date, expense_id;

SQLite permits window functions in the result set and ORDER BY; filtering their results is done in an outer query. [S1] Adjust the threshold and column names to match your data.

A practical checklist

  • Replace the example table and column names with those in your schema.

  • Choose the column that defines the running sequence, such as the date.

  • Add a stable tie-breaker when multiple rows can share the ordering value.

  • Use the explicit frame from UNBOUNDED PRECEDING through CURRENT ROW for a row-by-row accumulation.

  • Add an outer ORDER BY if displayed results should follow the calculation sequence.

  • Use an outer query when filtering on the calculated running total.

A few more useful answers

Does the window ORDER BY also sort the final results?

Not by itself. The window ordering defines the calculation sequence; use an outer ORDER BY to specify displayed row order. [S1]

Why include a tie-breaker in the window ordering?

When dates repeat, a tie-breaker such as an expense ID defines which tied row comes first in the calculation sequence. [S1]

Can I filter directly on running_total in the same query?

In SQLite, put the window calculation in a subquery, then filter on its result in the outer query. [S1]

Sources

References used for this guide.

  1. [S1] SQLite window functions: aggregate functions and window ordering
    sqlite.org · Checked

YOUR QUESTION. YOUR DETAILS.

Let’s work through your situation.

Get a tailored answer or help with your next task. Starts at $1, with a quote before you pay.

Ask your own question
← Back to Geez at Work