Advanced Case 40 of 50

Case 040: The Rolling Average

customersorderspayments

The running total from before keeps climbing forever, which makes it hard to tell if a customer's spending has actually shifted recently — one huge early order can bury a recent slowdown for months.

The manager wants something that reacts to recent behavior instead: "Running totals are useful, but they just keep climbing forever. I want a smoothed trend — each order's value averaged with the couple before it."

The data Alex is looking at

customers
idnamecountrysignup_date
1SamGermany2026-01-02
2NinaCanada2026-01-05
3DavidGermany2026-01-10
4MayaJapan2026-01-15
5DanielCanada2026-01-20
… 5 more rows
orders
idcustomer_idorder_date
10112026-01-05
10222026-01-08
10332026-01-12
10412026-01-20
10542026-02-01
… 23 more rows
payments
idorder_idamount
6011016200
6021023500
6031033600
60410460000
60510512000
… 23 more rows
TASK

Help Alex answer the manager:

“For each customer, show every order's date and amount, along with a rolling average of the last 3 orders (the current one and the 2 before it), in date order.”
SQL editor Ctrl+Enter to run
Write a query above and hit Run to see what comes back.
No hints requested yet — click "Get a hint" when you're stuck.
Alex's final query — this is for reference. Copy it into your own thinking, not into the editor above.
SELECT customers.name, orders.order_date, payments.amount, ROUND(AVG(payments.amount) OVER ( PARTITION BY customers.id ORDER BY orders.order_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW ), 1) AS rolling_avg_last_3 FROM customers JOIN orders ON customers.id = orders.customer_id JOIN payments ON payments.order_id = orders.id ORDER BY customers.name, orders.order_date;
What that query returns
Case Debrief Window frames (ROWS BETWEEN)
  • ROWS BETWEEN defines exactly which rows a window function looks at, relative to the current row — this case's ROWS BETWEEN 2 PRECEDING AND CURRENT ROW always covers exactly 3 rows: the current one and the 2 right before it.
  • A running total leaves that frame open (growing from the start of the partition with every row); a moving average bounds it to a fixed size like this one, so old orders eventually slide back out instead of piling up forever.

Case closed.

Ready for the next one?

Next: Case 041 — The Cross-Sell Opportunity →