Lesson 11 / 25
Running Totals, Moving Averages and LAG
Frames and offsets.
Window frames
With ORDER BY in the window, aggregates become cumulative: sum(amount) OVER (ORDER BY day) is a running total (the default frame is RANGE UNBOUNDED PRECEDING to CURRENT ROW, which includes peers with the same ordering value). An explicit frame such as ROWS BETWEEN 2 PRECEDING AND CURRENT ROW gives a moving average over three rows. LAG and LEAD read previous or next rows for period-over-period changes.
Running total, 3-row moving average and change, run
I ran this with psql on PostgreSQL 16.2 against the course sample data (shown in the first section). Each paid order adds to the running total; the moving average uses up to three rows; LAG is empty on the first row.
SELECT ordered_on, amount,
sum(amount) OVER (ORDER BY ordered_on) AS running_total,
round(avg(amount) OVER (ORDER BY ordered_on
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW), 2) AS moving_avg_3,
amount - LAG(amount) OVER (ORDER BY ordered_on) AS change_vs_prev
FROM orders WHERE status = 'paid'
ORDER BY ordered_on;
Output:
ordered_on | amount | running_total | moving_avg_3 | change_vs_prev ------------+---------+---------------+--------------+---------------- 2026-01-03 | 1200.00 | 1200.00 | 1200.00 | 2026-01-20 | 300.00 | 1500.00 | 750.00 | -900.00 2026-02-02 | 450.00 | 1950.00 | 650.00 | 150.00 2026-02-14 | 2000.00 | 3950.00 | 916.67 | 1550.00 2026-02-28 | 700.00 | 4650.00 | 1050.00 | -1300.00 (5 rows)
Use ROWS for exact row counts
RANGE frames group ties together, which surprises people when two rows share a date.
Quick check: Which function returns the previous row's value in the window order?
- NTILE
- LEAD
- FIRST_VALUE
- LAG
Answer
LAG — LEAD looks forward.