datarekha
SQL Medium Asked at GoogleAsked at AmazonAsked at MetaAsked at AppleAsked at Microsoft

Write a query to return the top 3 highest-paid employees in each department.

The short answer

Assign ROW_NUMBER() (or DENSE_RANK() if ties should be included) partitioned by department and ordered by salary descending, then filter in an outer query or CTE where the rank is 3 or less. You cannot filter on a window function directly in WHERE — it must be wrapped.

How to think about it

This is one of the most common SQL interview problems, and it is really testing three things at once: whether you know window functions, whether you know why you need a wrapper query, and whether you can reason about ties. Let’s build the answer step by step.

Why you need a wrapper query

Window functions are evaluated in the SELECT phase — after WHERE has already run. So you cannot write WHERE rn <= 3 in the same query that computes rn. You must wrap the ranked result in a CTE or subquery and filter in the outer query.

Step 1: assign a rank per department

SELECT
    dept,
    name,
    salary,
    ROW_NUMBER() OVER (
        PARTITION BY dept
        ORDER BY salary DESC
    ) AS rn
FROM employees;

PARTITION BY dept restarts the counter for each department. ORDER BY salary DESC gives rank 1 to the highest earner in each department.

Step 2: wrap and filter

WITH ranked AS (
    SELECT
        dept,
        name,
        salary,
        ROW_NUMBER() OVER (
            PARTITION BY dept
            ORDER BY salary DESC
        ) AS rn
    FROM employees
)
SELECT dept, name, salary
FROM ranked
WHERE rn <= 3
ORDER BY dept, salary DESC;

Run it yourself — and try changing ROW_NUMBER to DENSE_RANK to see what happens when salaries tie:

ROW_NUMBER vs DENSE_RANK vs RANK

RequirementFunction
Exactly N rows per dept, ties broken arbitrarilyROW_NUMBER
Include all employees tied for Nth placeDENSE_RANK
Include ties but skip ranks after themRANK

With DENSE_RANK and <= 3, if three people share the third salary you get all three — potentially more than 3 rows per department. That may or may not be what the business wants; clarify before you code.

The pre-window alternative (older shops still ask)

SELECT e1.*
FROM employees e1
WHERE (
    SELECT COUNT(DISTINCT e2.salary)
    FROM employees e2
    WHERE e2.dept = e1.dept
      AND e2.salary > e1.salary
) < 3;

This is O(n²) and fragile — state that you know it, then pivot immediately to the window function version.

Learn it properly Top-N per group

Keep practising

All SQL questions

Explore further

Skip to content