WindowSQL standard

NTH_VALUE

Takes the value from the Nth row of the window: the second, the third…

Syntax

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

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

Like FIRST_VALUE, but for any number: NTH_VALUE(salary, 2) is the second salary in the window. The number of rows stays the same, and each gets the value from its own window. If the window doesn't have an Nth row yet, the result is NULL.

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 second salary next to each one
SELECT name, salary,
NTH_VALUE(salary, 2) OVER (
ORDER BY salary DESC
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS second_salary
FROM employees;
namesalarysecond_salary
Olivia13501200
Emma12001200
Sophia12001200
Liam10001200
Noah9501200
Without a frame — NULL at the start
SELECT name, salary,
NTH_VALUE(name, 2) OVER (ORDER BY salary DESC) AS second_so_far
FROM employees;
namesalarysecond_so_far
Olivia1350NULL
Emma1200Emma
Sophia1200Emma
Liam1000Emma
Noah950Emma

Without a frame the window ends at the current row (together with its ties). For Olivia the window contains only her, so there's no second row yet.

The second name in each departmentAdvanced
SELECT name, department, salary,
NTH_VALUE(name, 2) OVER (
PARTITION BY department ORDER BY salary DESC
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS second_in_department
FROM employees;
namedepartmentsalarysecond_in_department
Emmabackend1200Liam
Liambackend1000Liam
Sophiadesign1200NULL
Oliviafrontend1350Noah
Noahfrontend950Noah

design has one person, so there's no second — NULL.

Watch out

Without the ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING frame, the first rows get NULL: the window ends at the current row, and there's no Nth row in it yet.

The second highest salary isn't always NTH_VALUE(salary, 2): if the top two are equal, the second row gives the same number as the first. The second distinct value is found by DENSE_RANK.

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