SQL interview question: Nth Highest Salary
Find the Nth Highest Salary
Several people at the company share the same salary, so a plain sort will count the same amount more than once.
Find every employee who earns the 6th highest distinct salary. Return first_name, last_name and salary.
Skills: DENSE_RANK, DISTINCT and LIMIT with OFFSET
Tables
employees 37 rows
- id integer PK
- first_name text
- last_name text
- email text
- department_id integer → departments
- salary integer
- hire_date text
- manager_id integer → employees
- title text
| id | first_name | last_name | department_id | salary | hire_date | manager_id | title | |
|---|---|---|---|---|---|---|---|---|
| 1 | Alice | Chen | [email protected] | 1 | 250000 | 2015-01-15 | NULL | CEO |
| 2 | Robert | Martinez | [email protected] | 2 | 180000 | 2016-03-20 | NULL | CEO |
| 3 | Sarah | Johnson | [email protected] | 1 | 150000 | 2017-06-10 | 1 | VP Engineering |
How to solve it
To find the Nth highest salary, rank the distinct salaries with DENSE_RANK() OVER (ORDER BY salary DESC) and keep the rows where the rank equals N. If you only need the amount, SELECT DISTINCT salary ... ORDER BY salary DESC LIMIT 1 OFFSET N - 1 also works.
Why the plain LIMIT trick breaks
The first query most people write is:
SELECT salary FROM employees
ORDER BY salary DESC
LIMIT 1 OFFSET 5;It skips five rows and returns the sixth. That is only right when every salary is different. In this table two people earn 140000 and three earn 135000, so skipping five rows lands on the second 140000 and returns the 5th highest amount, not the 6th. Interviewers pick data like this on purpose.
Three ways to get it right
1. DISTINCT with LIMIT and OFFSET. Remove the duplicates first, then skip N - 1 amounts:
SELECT DISTINCT salary FROM employees
ORDER BY salary DESC
LIMIT 1 OFFSET 5;This returns the amount only. It is short and easy to explain, which makes it a good first answer.
2. DENSE_RANK. Rank the amounts so ties share a rank, then filter:
WITH ranked AS (
SELECT first_name, last_name, salary,
DENSE_RANK() OVER (ORDER BY salary DESC) AS salary_rank
FROM employees
)
SELECT first_name, last_name, salary
FROM ranked
WHERE salary_rank = 6;This is the answer most interviewers want to see, because it also returns the people and handles ties without extra work. Swap ORDER BY salary DESC for ORDER BY salary to get the Nth lowest instead.
3. A correlated subquery. Keep the salary that has exactly N - 1 distinct amounts above it:
SELECT DISTINCT salary FROM employees e1
WHERE 5 = (SELECT COUNT(DISTINCT salary)
FROM employees e2
WHERE e2.salary > e1.salary);It works in databases without window functions, but it compares every row with every other row, so mention that it gets slow on big tables.
RANK, DENSE_RANK or ROW_NUMBER?
- DENSE_RANK gives ties the same rank and leaves no gaps (1, 2, 2, 3). Rank N is always the Nth distinct amount, which is what this question asks for.
- RANK gives ties the same rank but then skips numbers (1, 2, 2, 4). Rank 3 may not exist at all.
- ROW_NUMBER gives every row its own number, so it behaves like the broken LIMIT query above.
What to say in the interview
Ask two questions before writing anything: does "Nth highest" mean distinct amounts, and what should come back when fewer than N distinct salaries exist? Then write the DENSE_RANK version, mention the LIMIT and OFFSET shortcut, and explain why ROW_NUMBER would be wrong. For the per-department version of this question, add PARTITION BY department_id inside the OVER clause.
Common questions
- What is the difference between DENSE_RANK and RANK for the Nth highest salary?
- DENSE_RANK numbers tied salaries the same and continues with the next number, so rank N is always the Nth distinct salary. RANK skips numbers after a tie, so the rank you are looking for might not exist.
- How do I return NULL when there is no Nth highest salary?
- Wrap the query in a scalar subquery, for example
SELECT (SELECT DISTINCT salary FROM employees ORDER BY salary DESC LIMIT 1 OFFSET 5) AS nth_salary. When fewer than six distinct salaries exist, the subquery returns no row and the outer SELECT shows NULL. - How do I find the Nth highest salary in each department?
- Add PARTITION BY to the window:
DENSE_RANK() OVER (PARTITION BY department_id ORDER BY salary DESC). Then filter on the rank as before, and you get one result per department. - Does LIMIT and OFFSET work in SQL Server?
- SQL Server uses
OFFSET 5 ROWS FETCH NEXT 1 ROWS ONLYafter the ORDER BY instead of LIMIT. The DENSE_RANK version works unchanged in SQL Server, PostgreSQL, MySQL 8 and SQLite.
Practice on your iPhone
Query Dojo for iPhone has 85 interview questions, timed mock interviews and a tile builder, so you can write queries without a keyboard.
Other SQL dialects
- SQL Server Use OFFSET 5 ROWS FETCH NEXT 1 ROWS ONLY instead of LIMIT 1 OFFSET 5. DENSE_RANK works the same way.
- MySQL DENSE_RANK needs MySQL 8.0 or newer. On 5.7, use the subquery with SELECT DISTINCT and LIMIT 1 OFFSET 5.