Skip to content

Intermediate: combining and changing data · Window functions · topic 33 of 62 · 35 min

Ranking: ROW_NUMBER, RANK, DENSE_RANK and NTILE

Explanation

ROW_NUMBER, RANK and DENSE_RANK number the rows in the order given by ORDER BY inside OVER. They differ only on ties: ROW_NUMBER always gives 1, 2, 3, RANK gives 1, 1, 3 (it skips), and DENSE_RANK gives 1, 1, 2 (no gaps). PARTITION BY restarts the numbering in each group, and NTILE(n) splits the rows into n nearly equal buckets.

Why ranking functions?

Leaderboards, "top 3 sellers", "the 2nd highest salary" and "the latest order of each customer" all need a number that says where a row stands. You could sort and count by eye, but SQL can write the position on each row for you. That is what ranking functions do.

ORDER BY inside OVER

A ranking function needs to know what "first" means, so you put an ORDER BY inside the OVER brackets. This ORDER BY decides the numbering only. It does not sort the final result; for that you still need the normal ORDER BY at the end of the query. Ranking functions take no argument: you write ROW_NUMBER(), with empty brackets.

Three ways to handle ties

In COMPANY, Anita Desai and Vikram Singh both earn 95000. Watch what each function does with them.

sql
SELECT name, salary,
       ROW_NUMBER() OVER (ORDER BY salary DESC, name) AS row_num,
       RANK()       OVER (ORDER BY salary DESC)       AS rnk,
       DENSE_RANK() OVER (ORDER BY salary DESC)       AS dense
FROM employees
ORDER BY salary DESC, name;
-- Meera Iyer     | 250000 | 1 | 1 | 1
-- Rahul Sharma   | 120000 | 2 | 2 | 2
-- Pooja Gupta    | 110000 | 3 | 3 | 3
-- Farhan Ali     | 105000 | 4 | 4 | 4
-- Anita Desai    | 95000  | 5 | 5 | 5
-- Vikram Singh   | 95000  | 6 | 5 | 5
-- Lakshmi Pillai | 80000  | 7 | 7 | 6
-- ... 11 rows
The tie at 95000 is where the three functions part ways.
FunctionOn a tieAfter the tieUse it for
ROW_NUMBER()Different numbers (5, 6)Continues: 7Exactly one row per position: paging, de-duplication, "latest row"
RANK()Same number (5, 5)Skips: 7Sports-style ranks: two joint 5th, nobody is 6th
DENSE_RANK()Same number (5, 5)No gap: 6"The 2nd highest salary" counted by distinct values
Careful: ROW_NUMBER breaks ties arbitrarily. Without a tie-breaker, the database may give Anita 5 today and 6 tomorrow. Add a unique column at the end of the ORDER BY inside OVER (name or id) so the numbers are always the same.

Ranking inside groups

Add PARTITION BY and the numbering starts again at 1 in each partition. This answers "who is the best paid in each department?" in one pass.

sql
SELECT name, dept_id, salary,
       RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS dept_rank
FROM employees
WHERE dept_id IS NOT NULL
ORDER BY dept_id, dept_rank, name;
-- Meera Iyer     | 1 | 250000 | 1
-- Rahul Sharma   | 1 | 120000 | 2
-- Anita Desai    | 1 | 95000  | 3
-- Vikram Singh   | 1 | 95000  | 3
-- Pooja Gupta    | 2 | 110000 | 1
-- ...
Each department has its own 1, 2, 3.

NTILE: buckets

NTILE(n) deals the ordered rows into n buckets numbered 1 to n, as evenly as possible, like dealing cards. NTILE(4) gives quartiles: bucket 1 is the top quarter. If the rows do not divide evenly, the first buckets get one extra row. Seven students in four buckets gives sizes 2, 2, 2 and 1.

sql
SELECT name, marks, NTILE(4) OVER (ORDER BY marks DESC) AS quartile
FROM students
WHERE marks IS NOT NULL
ORDER BY marks DESC;
-- Priya Nair  | 91 | 1
-- Divya Kumar | 88 | 1
-- Karan Mehta | 79 | 2
-- Asha Verma  | 76 | 2
-- Ravi Kumar  | 67 | 3
-- Imran Khan  | 58 | 3
-- Arjun Reddy | 45 | 4
Sneha has no marks. Without the WHERE, her NULL would land in a bucket too (last with DESC in SQLite), which makes no sense.

NTILE looks only at row positions, not values: two equal marks can fall into different buckets. Also note that NULLs sort first in ascending order in SQLite, so decide on purpose what should happen to them, either filter them out or use NULLS LAST.

PERCENT_RANK and CUME_DIST in one line

PERCENT_RANK() gives (rank − 1) / (rows − 1), a number from 0 to 1 that says what fraction of rows rank below this one. CUME_DIST() gives the fraction of rows at or below this one. With ORDER BY salary, Lakshmi (80000) has a PERCENT_RANK of 0.4 among the 11 employees.

Common mistakes

  • Using ROW_NUMBER when ties matter. For "everyone who shares 1st place", use RANK or DENSE_RANK.
  • No tie-breaker in ROW_NUMBER, so results change between runs. Add a unique column.
  • Expecting the ORDER BY inside OVER to sort the output. Add an ORDER BY at the end of the query too.
  • Forgetting PARTITION BY and ranking the whole company when you wanted each department.
  • Leaving NULLs in a ranking without thinking. Filter them or place them with NULLS FIRST / NULLS LAST.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Ranking: ROW_NUMBER, RANK, DENSE_RANK and NTILE · 1/7

ROW_NUMBER, RANK, DENSE_RANK, NTILE

Ranking functions write a position number on every row. Leaderboards, top three lists and second highest salary questions all use them.

Prefer a video by a teacher? Find free videos on “sql row_number rank dense_rank ntile difference” on YouTube ↗

In real life: A merit list for college admissions

Admission offices publish merit lists where students with equal marks share a rank, and the next rank is skipped, exactly what RANK() does. Many also break ties by a second rule, such as the marks in a key subject or the date of birth, which becomes a second column in the ORDER BY inside OVER. Dividing applicants into bands for counselling rounds is an NTILE-style job.

sql
SELECT name, course, marks,
       RANK() OVER (PARTITION BY course ORDER BY marks DESC) AS merit_rank
FROM students
WHERE marks IS NOT NULL
ORDER BY course, merit_rank;
BCA: Priya 1, Asha 2, Arjun 3. BCom: Divya 1, Imran 2. BSc: Karan 1, Ravi 2.

Practice (0/4 done)

Task 1 · Code · SQL

Show the three ranking functions side by side. From employees, return name, salary, ROW_NUMBER() named row_num, RANK() named rnk and DENSE_RANK() named dense, all ordered by salary from highest to lowest. Break ties in ROW_NUMBER by name (A to Z). Sort the result by salary descending, then name.

Task 2 · Code · SQL

Rank employees inside their own department by salary, highest first, using RANK(). Leave out the employee with no department. Return name, dept_id, salary and the rank named dept_rank, sorted by dept_id, then dept_rank, then name.

Task 3 · Code · SQL

Split the students into 4 groups by marks with NTILE(4), the highest marks in group 1. The student with no marks (NULL) must be left out. Return name, marks and the group named quartile, sorted by marks from highest to lowest.

Task 4 · Code · SQL

Make a merit list per course. Leave out the student with no marks. Return course, name, marks and DENSE_RANK() by marks (highest first) inside each course, named course_rank, sorted by course, then course_rank.

Quick quiz

Question 1

1.Salaries in order are 300, 200, 200, 100. What does RANK() OVER (ORDER BY salary DESC) give?

Question 2

2.For the same salaries (300, 200, 200, 100), what does DENSE_RANK() give?

Question 3

3.Why add name to ROW_NUMBER() OVER (ORDER BY salary DESC, name)?

Question 4

4.Ten rows are split with NTILE(3). How many rows land in each bucket?

Question 5

5.Does the ORDER BY inside OVER (...) sort the rows of the final result?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

Salaries in order are 300, 200, 200, 100. What does RANK() OVER (ORDER BY salary DESC) give?

Answer: 1, 2, 2, 4

RANK gives ties the same number and then skips, so the next rank is 4.

For the same salaries (300, 200, 200, 100), what does DENSE_RANK() give?

Answer: 1, 2, 2, 3

DENSE_RANK gives ties the same number and leaves no gaps.

Why add name to ROW_NUMBER() OVER (ORDER BY salary DESC, name)?

Answer: It makes the numbering of tied salaries the same on every run

Without a tie-breaker, tied rows can be numbered in any order.

Ten rows are split with NTILE(3). How many rows land in each bucket?

Answer: 4, 3, 3

NTILE spreads rows as evenly as possible, and the first buckets get the extra rows.

Does the ORDER BY inside OVER (...) sort the rows of the final result?

Answer: No; it only decides the numbering, so add an ORDER BY at the end for the output order

The output order is set only by the query's own ORDER BY.

Two employees tie for the 2nd highest salary. Which function returns both of them for "rank = 2"?Related

Answer: DENSE_RANK

DENSE_RANK (and RANK) give ties the same rank; ROW_NUMBER numbers them 2 and 3.

From Classic interview queries, part 1

In a de-duplication with ROW_NUMBER() OVER (PARTITION BY email ORDER BY id), which rows are deleted to keep the lowest id?Related

Answer: rn > 1

Row 1 has the lowest id in each email and stays; the others are copies.

From Top-N per group and de-duplication

employees has 11 rows. How many rows does SELECT name, AVG(salary) OVER (PARTITION BY dept_id) FROM employees return?Related

Answer: 11

A window function never removes rows; each of the 11 rows gets its department average.

From OVER and PARTITION BY

Two employees tie on salary. How do you make ORDER BY salary DESC LIMIT 5 give the same rows every time?Related

Answer: Add a tie-breaker, such as ORDER BY salary DESC, id

A second sort column decides between tied rows.

From ORDER BY, LIMIT and OFFSET

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 3 more topics in “Window functions” before its exam: Running totals, moving averages and frames, LAG, LEAD, FIRST_VALUE and LAST_VALUE, Top-N per group and de-duplication.
  2. Module 7 exam: Window functions · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Changing data, transactions and ACID. 62 topics left in the course before the final exam.

← Previous: OVER and PARTITION BY