LAG() looks backward to a previous row; LEAD() looks forward
to a following row – both relative to the current row’s position in the
window’s ORDER BY sequence. They are the foundation of every
period-over-period comparison in analytics SQL.
LAG() / LEAD().01_ROW_NUMBER04_PARTITION_BYSELECT
column_a,
LAG(column_b, offset, default_value) OVER (ORDER BY sort_column) AS prev_value,
LEAD(column_b, offset, default_value) OVER (ORDER BY sort_column) AS next_value
FROM table_name;
offset defaults to 1 (one row back/forward); default_value defaults
to NULL for rows with no previous/next row.
employes
See 05_lag_lead.sql.
| Domain | Scenario |
|---|---|
| Finance | Month-over-month revenue growth |
| Retail | Previous day’s sales for a trend chart |
| Banking | Stock/asset price change tracking |
| Marketing | Customer activity gap detection |
ORDER BY inside OVER(), which makes “previous” and
“next” meaningless / non-deterministic.LAG() value (NULL) and the
last row has no LEAD() value (NULL) unless a default is supplied.LAG()/LEAD() across partition boundaries unintentionally –
always add PARTITION BY when “previous” should mean “previous within
this group” (e.g., previous month for this customer).default_value (third argument) when NULL would
break downstream arithmetic (e.g., default to 0 for growth deltas).LAG()/LEAD() with PARTITION BY whenever the comparison
should stay within a customer, account, or store rather than the
whole table.LAG()/LEAD() are evaluated in the same single sorted pass as other
window functions – no self-join is required, which is significantly
faster and more readable than the older JOIN table AS t1 ON t1.id = t2.id - 1
pattern.
LAG().LEAD().emp_id and the previous
emp_id (and repeat using a 2-row offset).Intermediate
25 minutes
LAG()/LEAD() confidently, including the offset and default
arguments.LAG().← Previous: 04_PARTITION_BY
↑ Module README
→ Next: 06_FIRST_LAST_NTILE