Month-over-month change in SQL with LAG (and the missing month)
The question: For each month, show revenue and the change from the month just before it. March has no row (the shop was closed); when the month before is missing, the change is NULL.
monthly_revenue
| month | revenue |
|---|---|
| 2026-01 | 1000 |
| 2026-02 | 1200 |
| 2026-04 | 900 |
| 2026-05 | 1350 |
This query looks right. Run it, decide if it is, and fix it.
SELECT month, revenue, revenue - LAG(revenue) OVER (ORDER BY month) AS change FROM monthly_revenue ORDER BY month;
Show the answer and why the first query is wrong
Why the usual answer is wrong
SELECT month, revenue, revenue - LAG(revenue) OVER (ORDER BY month) AS change FROM monthly_revenue ORDER BY month;
LAG takes the previous row, and the row before April is February. April shows -300, a drop from February that nobody can compare: March is missing.
The answer
SELECT m.month, m.revenue, m.revenue - p.revenue AS change
FROM monthly_revenue m
LEFT JOIN monthly_revenue p
ON p.month = strftime('%Y-%m', date(m.month || '-01', '-1 month'))
ORDER BY m.month;Join each month to the calendar month before it. When that month has no row, the LEFT JOIN gives NULL and so does the change.
LAG is fine when every month has a row. In an interview, ask: can a month be missing? In PostgreSQL, use a date column and month - interval '1 month'.
LAG, checking the month before
SELECT month, revenue,
CASE WHEN prev_month = strftime('%Y-%m', date(month || '-01', '-1 month'))
THEN revenue - prev_revenue END AS change
FROM (
SELECT month, revenue,
LAG(month) OVER (ORDER BY month) AS prev_month,
LAG(revenue) OVER (ORDER BY month) AS prev_revenue
FROM monthly_revenue
)
ORDER BY month;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.