WindowSQL standard

ROW_NUMBER

Numbers rows 1, 2, 3… in a given order — across the whole result or within each group.

Syntax

ROW_NUMBER() OVER (ORDER BY <sort key>)
ROW_NUMBER() OVER (PARTITION BY <group> ORDER BY <sort key>)

A window function: it doesn't reduce the number of rows but adds a number to each one. PARTITION BY restarts the numbering in each group — that's how you build “top N in each category”.

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.

Places by salary
SELECT name, salary,
ROW_NUMBER() OVER (ORDER BY salary DESC) AS place
FROM employees;
namesalaryplace
Olivia13501
Emma12002
Sophia12003
Liam10004
Noah9505

Emma and Sophia earn the same, but they get different numbers — chance decides who's second. More on this under “Watch out”.

Numbering restarts in each department
SELECT name, department, salary,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS place
FROM employees;
namedepartmentsalaryplace
Emmabackend12001
Liambackend10002
Sophiadesign12001
Oliviafrontend13501
Noahfrontend9502
The highest salary in each departmentAdvanced
SELECT name, department, salary
FROM (
SELECT name, department, salary,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS place
FROM employees
) ranked
WHERE place = 1;
namedepartmentsalary
Emmabackend1200
Sophiadesign1200
Oliviafrontend1350

You can't use the number in the WHERE of the same query, so we number the rows in a subquery first and filter outside it.

Watch out

Equal values still get different numbers, and which of them comes first is down to chance. Add a unique field such as id to ORDER BY to make the order stable.

If equal values should share a place, use RANK or DENSE_RANK.

Related functions

Learn more in the course