SQL Window Functions: LAG, LEAD, Deltas, and Time-Series Comparisons

Compare rows to prior or next rows with LAG and LEAD for change detection, retention, and trend analysis.

View
StandardDetailedCompact
Export
Copy the compact sheet, download it, or print it.
Download
`D` dense toggle · `C` copy all

LAG and LEAD basics

Reach backward or forward without a self join.

Previous row with LAG

Pull the previous value in order.

sqlANYlag
sql
SELECT day,
       revenue,
       LAG(revenue) OVER (ORDER BY day) AS prev_revenue
FROM daily_revenue;
Notes

`LAG` is the easiest way to compare the current row to the previous row.

Next row with LEAD

Preview the next value in sequence.

sqlANYlead
sql
SELECT day,
       revenue,
       LEAD(revenue) OVER (ORDER BY day) AS next_revenue
FROM daily_revenue;
Notes

Use `LEAD` for forward-looking comparisons or interval construction.

Provide a LAG default value

Avoid NULL on the first row.

sqlANYlagdefault
sql
SELECT day,
       revenue,
       LAG(revenue, 1, 0) OVER (ORDER BY day) AS prev_revenue_or_zero
FROM daily_revenue;
Notes

The third argument is the default when an offset row does not exist.

Look back two rows

Custom offset with LAG.

sqlANYoffset
sql
SELECT month,
       signups,
       LAG(signups, 2) OVER (ORDER BY month) AS signups_2_months_ago
FROM monthly_signups;
Notes

Offsets larger than 1 are useful for month-over-month or quarter-over-quarter comparisons.

Time-series patterns

Practical delta and retention examples.

Day-over-day absolute change

Subtract the previous day's value.

sqlANYchangedelta
sql
SELECT day,
       revenue,
       revenue - LAG(revenue) OVER (ORDER BY day) AS revenue_change
FROM daily_revenue;
Notes

A simple difference often matters more than the raw value.

Day-over-day percent change

Compute relative change versus previous row.

sqlANYpercent-change
sql
SELECT day,
       revenue,
       ROUND(
         100.0 * (revenue - LAG(revenue) OVER (ORDER BY day))
         / NULLIF(LAG(revenue) OVER (ORDER BY day), 0),
         2
       ) AS pct_change
FROM daily_revenue;
Notes

Use `NULLIF` to avoid division by zero.

Detect gaps between events

Measure time since the prior event.

sqlANYgapsevents
sql
SELECT user_id,
       event_time,
       event_time - LAG(event_time) OVER (
         PARTITION BY user_id
         ORDER BY event_time
       ) AS time_since_prev
FROM user_events;
Notes

The exact subtraction syntax varies by dialect, but the window pattern stays the same.

Preview next billing date or status

Use LEAD for forward state checks.

sqlANYsubscriptions
sql
SELECT customer_id,
       billing_month,
       status,
       LEAD(status) OVER (
         PARTITION BY customer_id
         ORDER BY billing_month
       ) AS next_status
FROM subscription_history;
Notes

Useful in churn modeling, handoff workflows, and lifecycle analysis.

Recommended next

No recommendations yet.