WindowSQL standard

LAST_VALUE

Takes the value from the last row of the window — but only with the right frame.

Syntax

LAST_VALUE(<expression>) OVER (
  ORDER BY <sort key>
  ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
)

LAST_VALUE(<expression>) OVER (
  PARTITION BY <group> ORDER BY <sort key>
  ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
)

Adds to every row the value from the last row of its window. The key detail is the frame: by default the window ends at the current row, so without ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING the “last” row is the current row itself. Its mirror function is FIRST_VALUE.

Data for the examples

employees

namedepartmentsalarybonusphone
Emmabackend1200300+44 7700 900123
Liambackend1000NULLNULL
Oliviafrontend1350150+44 7700 900456
Noahfrontend950NULL+44 7700 900789
Sophiadesign1200200NULL

These tables already exist in the sandbox — the examples below use them. Want to try this in your own database? Copy the SQL.

The lowest salary next to each one
SELECT name, salary,
LAST_VALUE(salary) OVER (
ORDER BY salary DESC
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS lowest
FROM employees;
namesalarylowest
Olivia1350950
Emma1200950
Sophia1200950
Liam1000950
Noah950950
With and without a frameAdvanced
SELECT name, salary,
LAST_VALUE(salary) OVER (ORDER BY salary DESC) AS without_frame,
LAST_VALUE(salary) OVER (
ORDER BY salary DESC
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS lowest
FROM employees;
namesalarywithout_framelowest
Olivia13501350950
Emma12001200950
Sophia12001200950
Liam10001000950
Noah950950950

Without a frame the window ends at the current row, so without_frame just repeats the salary. With the full frame, LAST_VALUE sees all the rows and returns the lowest — 950.

Who earns the least in each department
SELECT name, department, salary,
LAST_VALUE(name) OVER (
PARTITION BY department ORDER BY salary DESC
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS lowest_paid
FROM employees;
namedepartmentsalarylowest_paid
Emmabackend1200Liam
Liambackend1000Liam
Sophiadesign1200Sophia
Oliviafrontend1350Noah
Noahfrontend950Noah

Watch out

Without a frame, LAST_VALUE almost always returns the current value: the window ends at the current row. Add ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING.

It's often simpler to do without a frame: FIRST_VALUE(salary) OVER (ORDER BY salary) gives the same as LAST_VALUE with the full frame and DESC order.

You can't use the result in the WHERE of the same query — calculate it in a subquery first and filter outside it.

Related functions

Learn more in the course