Hook: Interviewers love LAST_VALUE because it looks obvious, but the real test is whether you know the window frame can stop at the current row.
Question: What does LAST_VALUE do in SQL?
Answer: LAST_VALUE returns the value from the last row in the current window frame, not automatically the last row in the whole partition. With the default frame used alongside ORDER BY, many databases stop the frame at the current row, so the result can look like the row you are already on.
Interview-Ready Answer: I use LAST_VALUE when I want the final value in an ordered window, but I always remember it works on the frame, not the whole partition by default. The big gotcha is that the default frame usually ends at the current row, so to get the true last value for the full group I add ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING. I also make the ORDER BY deterministic with a tie-breaker if timestamps can repeat.
Detailed Explanation: Think of a window function as a moving camera over a sorted partition of rows. LAST_VALUE(x) looks at the rows currently visible to that camera and returns the x value from the last visible row. That is why the frame matters so much: the function does not magically know you want the last row in the entire partition unless you tell it so.
PARTITION BY. Each group is handled separately.ORDER BY defines the logical order inside the group. Without a stable order, “last” is not meaningful.ORDER BY but no explicit frame, the default is typically RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW. That means the frame ends at the current row, not the end of the partition.LAST_VALUE then returns the expression from that row.ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING so every row can see the whole partition.Many candidates mentally read “last value” as “last row in the group.” But SQL window functions are framed. A frame is the subset of rows visible to the function for the current row. If that frame ends at the current row, then the last visible row is often the current row itself, which makes LAST_VALUE feel broken when it is actually doing exactly what you asked.
| Function | What it returns | Main gotcha |
|---|---|---|
LAST_VALUE | Last row in frame | Default frame can end at current row |
FIRST_VALUE | First row in frame | Needs a meaningful sort order |
MAX(...) OVER | Largest value in frame | Value order is not row order |
MAX; that is a different question.The expensive part is usually the sort. If a partition has 1,000 rows, this is trivial; if it has 1,000,000 rows, the engine may sort the partition first and then scan it. Roughly speaking, the work is O(n log n) for sorting plus O(n) for the window pass. In real systems, once the partition exceeds the memory grant or work area, the sort can spill to disk, and latency can jump from milliseconds to seconds. That is why indexes that match the partition and order keys can help a lot.
ORDER BY columns, add a unique column such as an id or timestamp+id pair.IGNORE NULLS; do not assume it exists everywhere.RANGE, rows with equal sort keys can act like one peer group, which changes what “last” means.Real-World Example: Imagine a checkout service that tracks order events: created, paid, packed, shipped, delivered. A product dashboard wants to show the final status next to every event so support can see where an order ended up. The engineer writes LAST_VALUE(status) OVER (PARTITION BY order_id ORDER BY event_time) and thinks it means final status.
In production, the dashboard starts showing the wrong answer: every row seems to carry its own status instead of the final status of the order. No SQL error appears, so the bug hides in plain sight. Support agents think orders are stuck at packed or shipped even after they were delivered, and the team wastes time chasing phantom delivery issues. The fix is simple but critical: add an explicit frame that reaches unbounded following, and add a tie-breaker if multiple events share the same timestamp.
The lesson is that a small misunderstanding in window framing can create a business bug, not just a query bug. The SQL runs, the numbers look plausible, and the bad result is exactly why LAST_VALUE is a favorite interview question.
WITH events(order_id, event_id, event_time, status) AS (
VALUES
(1, 1, CAST('2024-01-01 09:00:00' AS TIMESTAMP), 'created'),
(1, 2, CAST('2024-01-01 10:00:00' AS TIMESTAMP), 'paid'),
(1, 3, CAST('2024-01-01 12:00:00' AS TIMESTAMP), 'packed'),
(1, 4, CAST('2024-01-01 15:00:00' AS TIMESTAMP), 'shipped'),
-- Same timestamp as the prior row: this is the kind of tie that can make
-- an ORDER BY on time alone ambiguous unless you add a tie-breaker.
(1, 5, CAST('2024-01-01 15:00:00' AS TIMESTAMP), 'delivered'),
(2, 1, CAST('2024-01-02 08:30:00' AS TIMESTAMP), 'created'),
(2, 2, CAST('2024-01-02 09:00:00' AS TIMESTAMP), 'cancelled')
)
SELECT
order_id,
event_id,
event_time,
status,
-- Default frame behavior: this often ends at the current row, so the result
-- looks like the current row instead of the final row in the partition.
LAST_VALUE(status) OVER (
PARTITION BY order_id
ORDER BY event_time, event_id
) AS default_last_value,
-- Explicit full-partition frame: this is the "true last value" for the whole order.
LAST_VALUE(status) OVER (
PARTITION BY order_id
ORDER BY event_time, event_id
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS true_last_value
FROM events
ORDER BY order_id, event_time, event_id;Follow-up & Tricky Questions:
LAST_VALUE? With ORDER BY, the standard default is commonly RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW. That is why the function often returns the current row unless you extend the frame.ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING, or use a subquery that orders descending and picks the first row.LAST_VALUE different from MAX? LAST_VALUE follows row order; MAX follows value comparison. If the newest row is not the largest value, they return different answers.event_id so the order is deterministic. Otherwise the identity of the “last” row can vary.FIRST_VALUE instead? Use it when the important thing is the starting point of the group, such as the first price, first status, or original signup value.LAST_VALUE without ORDER BY? Technically some engines allow it, but “last” becomes meaningless because there is no defined order. In interviews, the safe answer is that you should always pair it with ORDER BY.LAST_VALUE ignore NULLs? Not by default in every database. Some engines support IGNORE NULLS, but behavior is vendor-specific, so you should check the target SQL dialect.Common Mistakes:
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING when you want the whole group.ORDER BY. Correct it by adding a tie-breaker column so the last row is deterministic.MAX. Correct it by asking whether you want the newest row or the largest value.Memory Hook: LAST_VALUE is the end of your flashlight beam, not the end of the hallway. If you do not widen the beam with an explicit frame, you only see up to the current row.
Cheat Sheet:
LAST_VALUE returns the last row in the current frame.ORDER BY often ends at CURRENT ROW.UNBOUNDED FOLLOWING for the true last row of the partition.LAST_VALUE is order-based; MAX is value-based.Practice Tasks: