Why interviewers love this: LEAD looks tiny, but it checks whether you can reason about ordered data row by row instead of only thinking in groups.
Question: What does LEAD do in SQL?
Answer: LEAD is a window function that returns a value from a following row in the same ordered result set. You usually pair it with OVER (PARTITION BY ... ORDER BY ...) so the database knows what counts as the next row. If there is no next row, it returns NULL unless you give a default value.
Interview-Ready Answer: I use LEAD when I want to look one row ahead in an ordered set, like comparing today’s value to tomorrow’s or finding the next event time. I define the order in the OVER clause, optionally split the data with PARTITION BY, and then LEAD returns the next row’s value, or NULL if there is no next row. A nice detail is that a default value only replaces a missing row, not a next row whose column value is actually NULL.
Detailed Explanation:
LEAD is a window function, which means it computes a value using the current row plus nearby rows while keeping every row in the result. A window is just the ordered set of rows visible to the function. Unlike GROUP BY, it does not merge rows into one summary row.
PARTITION BY. Each partition is handled separately, like its own mini table.ORDER BY list. This step matters because LEAD only makes sense when the row order is known.1, so plain LEAD(x) means next row.NULL, the result is still NULL.NULL.One useful mental detail: the window frame is the exact slice of rows a window function can see. That matters for aggregates like SUM() OVER, but for LEAD, the key idea is simply the ordered partition and the offset ahead.
| Technique | Best use | Main trade-off |
|---|---|---|
| LEAD | Look ahead | Needs ordering |
| LAG | Look behind | Opposite direction |
| Self join | Custom matching | More verbose |
LEAD is usually clearer than a self join because you do not have to manually match each row to the next one. It is also symmetrical with LAG, which looks backward instead of forward.
The expensive part is usually sorting, not the lookup itself. A common mental model is O(n log n) for the sort and then O(n) to walk the rows, so the total is dominated by ordering. On large partitions, the sort can spill to disk if memory is tight; in PostgreSQL, for example, that can happen when the working memory limit is too small. An index on the partition and order columns can help some engines read rows in the needed order and avoid an extra sort.
ORDER BY, the idea of a next row is undefined, so results are not reliable.ORDER BY has ties, add a tie-breaker such as a unique id, or the next row may change between runs.NULL or the default value.LEAD does not skip NULL values.NULL.Real-World Example: Imagine an e-commerce checkout service that stores each customer’s event stream: cart created, payment started, payment succeeded, receipt sent. Analysts use LEAD to compare each event time to the next event time and measure delays between steps.
It works well until a team orders events only by timestamp and two events land in the same second. Then the next row becomes unstable because the order is not fully deterministic. The symptom is weird: one run says a customer moved from payment to receipt in 2 seconds, another run says 9 seconds, and fraud alerts or abandoned-cart emails fire inconsistently. In the logs you may see changing row order for the same user, support tickets about duplicate emails, and dashboard numbers that do not match between refreshes. The fix is to add a stable tie-breaker, such as an event id, to the ORDER BY list.
-- LEAD looks ahead inside each customer's ordered history.
-- The last row in each partition has no next row, so it returns NULL unless we provide a default.
-- Also note: a NULL stored in the table is different from a missing next row.
DROP TABLE IF EXISTS sales;
CREATE TABLE sales (
customer_id INTEGER NOT NULL,
sale_date DATE NOT NULL,
amount DECIMAL(10,2)
);
INSERT INTO sales (customer_id, sale_date, amount) VALUES
(1, DATE '2024-01-01', 100.00),
(1, DATE '2024-01-03', 150.00),
(1, DATE '2024-01-05', 120.00),
(2, DATE '2024-01-02', 50.00),
(2, DATE '2024-01-04', NULL);
SELECT
customer_id,
sale_date,
amount,
LEAD(amount) OVER (
PARTITION BY customer_id
ORDER BY sale_date
) AS next_amount,
LEAD(amount, 1, 0.00) OVER (
PARTITION BY customer_id
ORDER BY sale_date
) AS next_amount_with_default,
LEAD(amount, 2, -1.00) OVER (
PARTITION BY customer_id
ORDER BY sale_date
) AS amount_two_steps_ahead,
LEAD(sale_date) OVER (
PARTITION BY customer_id
ORDER BY sale_date
) AS next_sale_date
FROM sales
ORDER BY customer_id, sale_date;Follow-up & Tricky Questions:
ORDER BY inside OVER? Because LEAD needs a defined row sequence. Without order, there is no reliable idea of which row comes next.LEAD and LAG? LEAD looks forward to a later row; LAG looks backward to an earlier row. They are mirror images and are often used together for change detection.NULL from the next row.LEAD cross partitions? No. Each partition is isolated, so the last row of one partition never looks into the next partition.PARTITION BY, what happens? The entire result set becomes one partition, so LEAD looks ahead across all rows in the whole query result.LEAD skip nulls automatically? No. It returns the next row exactly as stored, even if that value is NULL.Common Mistakes:
ORDER BY. Correction: always define a deterministic order, or the next row is meaningless.LEAD with LAG. Correction: LEAD looks forward, LAG looks backward.Memory Hook: Think of rows as people in a line: LEAD asks, Who is in front of me?
and LAG asks, Who is behind me?
Cheat Sheet:
LEAD(expr) means the next row’s value.PARTITION BY resets the look-ahead inside each group.ORDER BY defines what next means.Practice Tasks:
LAG and compare the output.