Code Lab is in development. New lessons and improvements are on the way. Progress can be deleted.
Loading…
Code Lab is in development. New lessons and improvements are on the way. Progress can be deleted.
Loading…
Code Lab is in development. New lessons and improvements are on the way. Progress can be deleted.
SQL / POSTGRESQL / Learn / LESSON 11
Capstone: a fixed period, CTE steps, date_trunc, LAG and a safe change calculation. LAG(revenue) OVER (ORDER BY month_start) returns the value from the PREVIOUS AVAILABLE row in the window order — not from the previous calendar month. If a month has no orders it is simply missing as a row, and LAG will point at the month before it. The first row has no predecessor, so LAG returns NULL there.
That is why the change must be guarded: difference = revenue - prev_revenue is NULL for the first month; the percentage 100.0 * (revenue - prev_revenue) / prev_revenue must handle both NULL and division by zero: CASE WHEN prev_revenue IS NULL OR prev_revenue = 0 THEN NULL ELSE ... END. Round with ROUND(x, 2).
You cannot filter on LAG in the same SELECT — compute it in a CTE, then filter in the outer query. "Top N" works the same way: the CTE computes the changes and the outer query adds ORDER BY ... LIMIT N. The window ORDER BY does not sort the result — a final ORDER BY is required for a predictable display.
Common mistakes: assuming LAG means "the previous month" when data is missing; dividing by zero; leaving the first row's NULL unguarded; forgetting the final ORDER BY; using <= instead of < the next first day.
Small example:
SELECT order_date, total, LAG(total) OVER (ORDER BY order_date, id) AS prev_total FROM orders ORDER BY order_date, id;
Working tables for this task. Grading also uses other datasets with the same schema.
| id | name | category | price |
|---|---|---|---|
| 1 | Atlas | books | 30 |
| 2 | Novel | books | 15 |
| 3 | Comic | books | 14.99 |
| 4 | Lamp | home | 29.99 |
| 5 | Vase | home | 30 |
| 6 | Rug | home | 45 |
| 7 | Hammer | tools | 15 |
| 8 | Drill | tools | 60 |
| 9 | Tape | tools | 15 |
| 10 | Candle | NULL | 8.5 |
| 11 | Gift Card | NULL | 25 |
| id | name | city |
|---|---|---|
| 1 | Mira | Sofia |
| 2 | Niko | Varna |
| 3 | Iva | Sofia |
| 4 | Alex | NULL |
| 5 | Boris | Plovdiv |
| 6 | Lena | Varna |
| 7 | Petya | Sofia |
| id | customer_id | total | order_date |
|---|---|---|---|
| 1 | 1 | 120 | 2024-01-05 |
| 2 | 1 | 35.5 | 2024-01-20 |
| 3 | 2 | 40 | 2024-01-20 |
| 4 | 3 | 99.99 | 2024-01-31 |
| 5 | NULL | 60 | 2024-02-01 |
| 6 | 1 | 40 | 2024-02-01 |
| 7 | 2 | 75.25 | 2024-02-14 |
| 8 | 3 | 100 | 2024-02-15 |
| 9 | 5 | 22 | 2024-02-15 |
| 10 | 1 | 39.99 | 2024-02-29 |
| 11 | 2 | 55 | 2024-03-01 |
| 12 | 3 | 18 | 2024-03-10 |
| 13 | 1 | 130 | 2024-03-15 |
| 14 | 2 | 40 | 2024-03-15 |
| 15 | 2 | 12.5 | 2024-03-31 |
| 16 | 3 | 47 | 2024-04-01 |
Expected columns: month_start, revenue, prev_revenue, revenue_change, change_pct · row order is checked
Expected result (displayed data):
Loading…Capstone: a fixed period, CTE steps, date_trunc, LAG and a safe change calculation. LAG(revenue) OVER (ORDER BY month_start) returns the value from the PREVIOUS AVAILABLE row in the window order — not from the previous calendar month. If a month has no orders it is simply missing as a row, and LAG will point at the month before it. The first row has no predecessor, so LAG returns NULL there. That is why the change must be guarded: difference = revenue - prev_revenue is NULL for the first month; the percentage 100.0 * (revenue - prev_revenue) / prev_revenue must handle both NULL and division by zero: CASE WHEN prev_revenue IS NULL OR prev_revenue = 0 THEN NULL ELSE ... END. Round with ROUND(x, 2). You cannot filter on LAG in the same SELECT — compute it in a CTE, then filter in the outer query. "Top N" works the same way: the CTE computes the changes and the outer query adds ORDER BY ... LIMIT N. The window ORDER BY does not sort the result — a final ORDER BY is required for a predictable display. Common mistakes: assuming LAG means "the previous month" when data is missing; dividing by zero; leaving the first row's NULL unguarded; forgetting the final ORDER BY; using <= instead of < the next first day.
QUERY STANDBY
›