DevLearningTools

LEARN · SQL

SQL FIRST_VALUE() and LAST_VALUE()

FIRST_VALUE() and LAST_VALUE() return a value from the first or last row of the window frame, not necessarily the partition. LAST_VALUE()'s default frame is the single most common window function mistake.

HR wants every employee's row to also show their department's top earner's name, for quick comparison. FIRST_VALUE() does that correctly on the first try. Asking for the department's lowest earner the same way, with LAST_VALUE(), is where almost everyone gets a confusing, wrong-looking result the first time.

Learning Objectives

  • Define FIRST_VALUE() and LAST_VALUE(), and what "first" and "last" actually mean, relative to the frame, not the partition.
  • Reproduce, and fix, LAST_VALUE()'s classic default-frame mistake.
  • Write a window frame explicitly, using ROWS BETWEEN.

Ordered Partition

salary DESC, within each department

Window Frame

Which rows count as 'the window', not just the partition

FIRST_VALUE / LAST_VALUE

Reads the frame's first or last row

Result

Correct only if the frame actually covers what's expected

What FIRST_VALUE() and LAST_VALUE() Actually Return

Both return a value from a specific row relative to the current row's window frame, the actual range of rows the function considers, not necessarily the entire partition. FIRST_VALUE() returns the frame's first row's value, LAST_VALUE() returns the frame's last row's value. The distinction between "frame" and "partition" is exactly where LAST_VALUE() goes wrong by default.

FIRST_VALUE() Works as Expected

This correctly shows the department's top earner's name on every row. It works because the default frame, when ORDER BY is present, runs from the partition's start up through the current row, and the current row is always at or after the frame's start, so FIRST_VALUE() always reaches that true first row correctly.

MySQL / MariaDB / PostgreSQL / SQL Server / Oracle / SQLite
SELECT first_name, department_id, salary,
       FIRST_VALUE(first_name) OVER (PARTITION BY department_id ORDER BY salary DESC) AS top_earner
FROM employees;

LAST_VALUE() Doesn't, By Default

This does not return the department's lowest earner. The default frame (UNBOUNDED PRECEDING to CURRENT ROW, whenever ORDER BY is present) means the frame's "last row" is always the current row itself, so lowest_earner just echoes each row's own first_name right back, not the actual lowest-paid person in the department.

Looks right, but isn't
SELECT first_name, department_id, salary,
       LAST_VALUE(first_name) OVER (PARTITION BY department_id ORDER BY salary DESC) AS lowest_earner
FROM employees;

The Fix: an Explicit Frame

MySQL / MariaDB / PostgreSQL / SQL Server / Oracle / SQLite
SELECT first_name, department_id, salary,
       LAST_VALUE(first_name) OVER (
         PARTITION BY department_id ORDER BY salary DESC
         ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
       ) AS lowest_earner
FROM employees;
NOTE

ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING explicitly extends the frame to cover the entire partition, every row before and after the current one, not just up to the current row. With that frame, LAST_VALUE() finally returns the true lowest earner on every row, correctly.

Try It Yourself

Fix this query so it correctly shows the most recently hired employee's name in every row of each department, on PostgreSQL: SELECT first_name, department_id, hire_date, LAST_VALUE(first_name) OVER (PARTITION BY department_id ORDER BY hire_date) AS most_recent_hire FROM employees;

Answer
SELECT first_name, department_id, hire_date,
       LAST_VALUE(first_name) OVER (
         PARTITION BY department_id ORDER BY hire_date
         ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
       ) AS most_recent_hire
FROM employees;

Frequently Asked Questions

Why doesn't FIRST_VALUE() have this same problem?

Because the default frame always starts at the partition's actual first row (UNBOUNDED PRECEDING) and ends at the current row, the frame's start never changes regardless of which row is current. The frame's end is what moves (it's always the current row), and that's specifically what LAST_VALUE() reads, which is why only LAST_VALUE() is affected.

Does this default-frame behavior apply when there's no ORDER BY at all?

No, with no ORDER BY, the default frame covers the entire partition from the start, so LAST_VALUE() actually does return the partition's true last row in that case. The gotcha specifically shows up once an ORDER BY is added, which is also exactly when ranking and sequencing usually become relevant in the first place.

Common Beginner Mistakes

Trusting LAST_VALUE() with ORDER BY and no explicit frame

This is the single most common window function mistake across all six databases in this course, the default frame quietly makes LAST_VALUE() return the current row's own value instead of the partition's actual last row. Always pair LAST_VALUE() with an explicit ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING whenever ORDER BY is present.

Assuming "first" and "last" refer to the whole partition

They refer to the current row's window frame specifically, which defaults to something narrower than the full partition once ORDER BY is involved. The frame, not the partition, is what FIRST_VALUE() and LAST_VALUE() actually read from.

Best Practices

  • Always write an explicit ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING frame alongside LAST_VALUE() whenever ORDER BY is present.
  • Test FIRST_VALUE()/LAST_VALUE() results against a row you can verify by eye before trusting them in a larger query.
  • When in doubt about frame behavior, an explicit frame is always safer than relying on the default.

Interview Questions

Why does LAST_VALUE() often return the wrong result, and how is it fixed?

Whenever ORDER BY is present inside OVER() and no frame is explicitly given, the default frame runs from the partition's start to the current row, UNBOUNDED PRECEDING AND CURRENT ROW, which means the frame's own "last row" is always the current row itself. LAST_VALUE() then just returns the current row's own value, not the partition's true last row. Adding ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING explicitly extends the frame across the whole partition, fixing it.

Why doesn't FIRST_VALUE() need the same fix as LAST_VALUE()?

The default frame's start is always UNBOUNDED PRECEDING, the true first row of the partition, regardless of which row is currently being evaluated, so FIRST_VALUE() always has access to the real first row by default. It's the frame's end, which LAST_VALUE() depends on, that defaults to the current row instead of the partition's true end.

Summary

FIRST_VALUE() and LAST_VALUE() both read from the window frame, not necessarily the whole partition, a distinction that only matters once ORDER BY is involved. FIRST_VALUE() works correctly by default because the frame's start is always the true first row. LAST_VALUE() does not, its default frame ends at the current row, not the partition's true last row, requiring an explicit ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING to fix.

What's Next?

SQL Running Totals and Moving Averages is next: SUM() and AVG() as window functions, where, unlike LAST_VALUE(), the default frame is actually exactly what's needed.

</>

Ready to Practice SQL?

Write and run real SQL queries online, right in your browser, no install or signup required. Pick a playground based on which database you want to practice on.

SQLite / PostgreSQL

Fast, no server, no ads.

Open DB Fiddle ↗

MySQL / SQL Server

Switch databases with one dropdown.

Open SQL Fiddle ↗

Both open a third-party SQL playground in a new tab. External site terms and privacy policy apply.

Or try 50 SQL practice questions →