chaturIQ
Thinking habit: Check what's missing

Rolling average in SQL (and the missing-day trap)

The question: From 3 March, show each day's average daily sales over that day and the 2 days before. A day with no row had no sales (0).

daily_sales

dayamount
2026-03-0130
2026-03-0260
2026-03-0390
2026-03-0560
2026-03-0630

This query looks right. Run it, decide if it is, and fix it.

Show the answer and why the first query is wrong

Why the usual answer is wrong

SELECT day, ROUND(avg_3, 2) AS avg_3_days
FROM (
  SELECT day,
    AVG(amount) OVER (ORDER BY day ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS avg_3
  FROM daily_sales
)
WHERE day >= '2026-03-03'
ORDER BY day;

4 March has no row, so for 5 March the window takes 2, 3 and 5 March: four calendar days, and the empty day isn't counted as 0.

The answer

SELECT d.day,
  ROUND((SELECT SUM(x.amount) FROM daily_sales x
         WHERE x.day BETWEEN date(d.day, '-2 day') AND d.day) / 3.0, 2) AS avg_3_days
FROM daily_sales d
WHERE d.day >= '2026-03-03'
ORDER BY d.day;

ROWS counts rows, not days. Add up the sales whose date falls in the last 3 calendar days and divide by 3, so a missing day counts as 0.

In real work you'd join to a calendar table so every day has a row. In PostgreSQL, a window with RANGE BETWEEN INTERVAL '2 days' PRECEDING AND CURRENT ROW works on dates.

Practise more like this, on real business data

324 interview-style questions on 35 tables with the duplicates, NULLs and missing weeks interviewers test. Free.

Open the lesson

More SQL interview questions