
📎 This article includes 2 downloadable practice files ↓
In this article
LAG looks at the previous row, LEAD at the next one, within the window’s order.
WITH m AS (SELECT month, SUM(amount) AS total FROM sales GROUP BY month)
SELECT month, total,
LAG(total) OVER (ORDER BY month) AS prev,
ROUND(100.0 * (total - LAG(total) OVER (ORDER BY month))
/ LAG(total) OVER (ORDER BY month), 1) AS growth_pct
FROM m;
The first month has no previous month, so prev and growth are NULL. That’s correct, not an error.
Gaps between orders
SELECT order_id, order_date,
julianday(order_date) - julianday(LAG(order_date) OVER (ORDER BY order_date)) AS gap_days
FROM orders WHERE customer_id = 1;
Date maths differs by database: SQLite julianday(a) - julianday(b), PostgreSQL a - b, SQL Server DATEDIFF(day, b, a), MySQL DATEDIFF(a, b).
LAG(total, 12) looks 12 rows back: year-on-year growth on monthly data. Add a default as the third argument, LAG(total, 1, 0), to avoid NULLs.Common mistakes
- Dividing by a zero previous month: wrap the divisor in
NULLIF(..., 0). - Missing months: LAG takes the previous row, not the previous month. Fill gaps with a calendar (lesson 7) first.
Practice
Download shop2.db and this lesson’s exercise file below. Open the database in DB Browser for SQLite (free), paste each task into the Execute SQL tab and compare with the expected result printed under it. Answers are at the bottom of the file.
📎 Practice files for this article
⬇ Download all 2 files (ZIP · 9 KB)
- 📄Practice database (SQLite)shop2.db: customers, products, 300 orders, payments, employees with managers and monthly targets.⬇ DB · 48 KB
- 🗃️Lesson 4 exercisesTasks with the real expected result under each, and answers at the bottom.⬇ SQL · 3 KB
Free to use for learning. Files with macros (.bas) are plain text — import them with Alt+F11 → File → Import File, and always test on a copy.
Stuck on a step? Ask a question and the AI answers using this article.