Tier 4 · Advanced

FIRST_VALUE, LAST_VALUE, NTH_VALUE in SQL

By Owen Middleton · Updated September 2026 · Examples run on PostgreSQL 17

What are FIRST_VALUE and LAST_VALUE in SQL?

FIRST_VALUE, LAST_VALUE, and NTH_VALUE pick up the actual column value sitting at a specific position in an ordered partition and attach it to every row in the group.

You already know window functions can compute running totals or assign ranks. These three do something different: they broadcast a value from one specific position across the entire partition. Every row gets that same value alongside its own data. The most common use is attaching context from one row to an entire group — like tagging every order with the amount of the customer's first purchase.

SELECT customer_id, ordered_at::date, total_amount,
  FIRST_VALUE(total_amount) OVER (
    PARTITION BY customer_id ORDER BY ordered_at
    ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
  ) AS first_order_amount
FROM orders
ORDER BY customer_id, ordered_at
LIMIT 10

Every row for each customer receives the total_amount of their first order. The ORDER BY inside OVER decides which row is "first" — here, the earliest ordered_at. FIRST_VALUE picks up that row's value and puts it on every other row in the partition — no join needed.

Why does LAST_VALUE return the current row?

LAST_VALUE surprises almost everyone

LAST_VALUE seems like it should return the value from the final row of the partition. By default, it doesn't.

The default behavior is to look only as far as the current row. So for each row, "last" means the last row seen so far — which is the current row itself. LAST_VALUE ends up returning the current row's own value for most of the partition, which is rarely what you want.

To get the actual last row of the partition, extend the frame explicitly:

LAST_VALUE(event_type) OVER (
    PARTITION BY session_id
    ORDER BY event_time
    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
)

The extra line tells the function to look all the way to the end of the partition, not just to the current row. With that in place, every row in the session receives the final event type.

How do you get the nth row of a partition in SQL?

NTH_VALUE

NTH_VALUE(revenue, 3) returns the value at a specific position in the partition — in this case, the third row by the ORDER BY sequence. Position counting starts at 1. If the partition has fewer rows than the requested position, it returns NULL.

NTH_VALUE has the same quirk as LAST_VALUE: if the target position is ahead of the current row, the function returns NULL unless you tell it to look forward. The safe pattern when targeting any fixed position is to cover the full partition:

NTH_VALUE(revenue, 3) OVER (
    PARTITION BY region
    ORDER BY revenue DESC
    ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
)

This returns the third-highest revenue for each region, attached to every row in that region. The frame clause is the mechanism — N044 covers it in depth. For now, use this pattern whenever LAST_VALUE or NTH_VALUE looks like it's returning the wrong row.

Practice FIRST_VALUE and LAST_VALUE in SQL

Practice · easy ecommerce · Brightlane

Brightlane's CRM team is building a customer order history view. Every order should be annotated with that customer's initial purchase amount for easy reference.

Write a query to return every order's ID, customer ID, order amount, and the amount of that same customer's very first order chronologically.

Assumptions:

  • The orders table has one row per order with an id, a customer_id, a total_amount, and an ordered_at timestamp.
  • A customer's first order is the order with the smallest ordered_at for that customer_id. The same first-order amount appears on every row sharing a customer_id.
  • The final result is sorted by customer_id ascending, then by ordered_at ascending.

Output:

  • One row per order, with columns id, customer_id, total_amount, and first_order_amount. Sorted by customer_id, then ordered_at.
Schema · ecommerce5 tables? = nullable
categories
idinteger
nametext
parent_id?integer
products
idinteger
nametext
category_id?integer
pricenumeric
stock_qtyinteger
attributes?jsonb
order_items
idinteger
order_id?integer
product_id?integer
quantityinteger
unit_pricenumeric
customers
idinteger
nametext
emailtext
city?text
countrytext
created_attimestamptz
is_activeboolean
orders
idinteger
customer_id?integer
ordered_attimestamptz
statustext
total_amountnumeric

Run previews · Check grades

Write a query, then run it to see results here.

Worked solution

The full breakdown walks through the shape, each clause, why this approach beats the alternatives, and the trap to avoid.

See the full worked solution

9 FIRST_VALUE and LAST_VALUE practice problems

Start learning to practice all 9 FIRST_VALUE and LAST_VALUE problems, with instant grading and mastery tracking.

Common questions about FIRST_VALUE and LAST_VALUE

What does NTH_VALUE return when the position is past the end?

NULL. Asking for the ninth row of a two-row partition is not an error, so a report can quietly fill with NULLs when the groups are smaller than you assumed. Check the group sizes before trusting a fixed position.

Does FIRST_VALUE need a frame clause?

No. The default frame already starts at the beginning of the partition, so the first row is in view from the outset. LAST_VALUE is the one that needs a frame, because the default stops at the current row rather than the end.

Do these functions need an ORDER BY inside OVER?

They run without one, which is the trap. With no ordering the partition has no first or last in any meaningful sense, so you get a value from an arbitrary row rather than an error telling you something is missing.

How you actually get good at SQL

Reading explains SQL. Writing it, over and over with instant feedback, is what makes you fluent.

That's the whole SQLMaxx loop: 600+ real problems, instant AI feedback, mastery you can actually see, and spaced review that won't let you forget.

A stack of SQL practice problem cards, the top card showing an employees table.
615 problems · 66 concepts

Real problems. Not toy examples.

615 hand-built problems spanning all 66 concepts, from basic SELECTs to window functions, built on real schemas and real business questions, the kind you'll actually get asked on the job. Enough reps to make SQL automatic.

A retro computer showing a SQL query marked correct with a green checkmark.
Instant AI feedback

Write a query. Know if it's right in one second.

No copying an answer and hoping it clicked. The AI grader checks your real query against real data, catches exactly what's wrong, and explains the fix in plain English, like a senior analyst reading over your shoulder on every problem.

A circular mastery progress dial filling from blue to green, the SQLMaxx diamond at its center.
Mastery tracking

Stop guessing whether you actually know it.

SQLMaxx tracks every concept and shows you what you've mastered and what's still shaky. Your skills fill in one concept at a time, so 'I think I get joins' becomes something you can prove.

A SQL query editor circled by a blue return arrow with a clock, scheduled to come back for review.
Spaced review

Learn it once. Keep it for good.

Most of what you learn this week fades by next week. So when a concept comes due for review, SQLMaxx hands you a fresh problem to solve from a blank editor, not a flashcard to re-read. A research-backed spaced-repetition algorithm (FSRS) times each return for right before you'd forget, so your SQL is still there months later, when the interview or the job actually needs it.