Phase 8 · SQL interview problems, from easy to hard · Topic 8.7

The highest-paid employee in each department

Must knowInterview problemMedium

For each department, return the employee (or employees, if tied) with the highest salary. Show the department name, the employee's name and their salary.

Not started

Step 1 Solve it yourself first

Tables you will use

employees departments

What it really tests

The most common -function question: , including how to handle ties and how to filter on a window function.

The pattern: Top N per group

"In each", "per", "for every" together with "highest", "top 3", "most recent", "first".

Picture it

flowchart LR
  E["employees"] --> P["PARTITION BY dept_id<br/>ORDER BY salary DESC"]
  P --> D1["Engineering: Asha 1, Hana 2, Ben 3, Chen 3"]
  P --> D2["Sales: Dara 1, Eli 2"]
  P --> D3["Marketing: Fatima 1"]
  D1 --> K["keep rank 1 in each"]
  D2 --> K
  D3 --> K
Rank inside each department separately, then keep the ranked 1. Every department gets its own number one.

The approach, step by step

  1. Ask: if two people tie for top salary in a department, show both? (Yes here.)
  2. Rank employees within each department by salary, highest first: RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC).
  3. keeps ties together; would pick one at random.
  4. Do the ranking in a , then keep rank 1 outside it, and departments for the name.

Your turn: write it before you look

Ctrl/⌘ + Enter runs it. Select part of the query to run only that part.
Show the solution

Stuck for more than ten minutes? Open it, read it, close it, and write it again from memory.

Solution: RANK in a CTE, filter outside

sql
WITH ranked AS (
  SELECT d.dept_name, e.name, e.salary,
         RANK() OVER (PARTITION BY e.dept_id ORDER BY e.salary DESC) AS rnk
  FROM employees e
  JOIN departments d ON d.dept_id = e.dept_id
)
SELECT dept_name, name, salary
FROM ranked
WHERE rnk = 1
ORDER BY dept_name;

3 rows

dept_namenamesalary
EngineeringAsha95000
MarketingFatima52000
SalesDara60000

Another way: Without window functions: match the department maximum

sql
SELECT d.dept_name, e.name, e.salary
FROM employees e
JOIN departments d ON d.dept_id = e.dept_id
WHERE e.salary = (
  SELECT MAX(e2.salary)
  FROM employees e2
  WHERE e2.dept_id = e.dept_id
)
ORDER BY d.dept_name;

3 rows

dept_namenamesalary
EngineeringAsha95000
MarketingFatima52000
SalesDara60000

Also keeps ties. Harder to extend to "top 3" than the window version.

Edge cases to say out loud

  • Ties for the top: RANK (or ) returns all of them; ROW_NUMBER returns one.
  • Departments with no employees (HR): not in the result. Ask whether they should appear with (then from departments).
  • Employees with no department (Gus): dropped by the to departments.

Common mistakes

  • Putting the window function in .
  • GROUP BY dept_id with MAX(salary) and name in — the name isn't guaranteed to belong to the max.
  • Using ROW_NUMBER when ties should all be returned.

Step 2 Go further (free, optional)

Optional. The notes above are written to be enough on their own. These free resources give a second explanation and more practice.

Step 3 Practise

Try each one yourself first: out loud, or written and run in the practice box below, which saves every query you run. Then open the answer and compare.

  1. ExplainSay what it does and when to use it, in plain words, with no notes.

    Explain why the rank must be computed in a CTE and filtered outside.

    Show answer

    Because of the order a runs in. WHERE runs before SELECT, and window functions like RANK are worked out in the SELECT step. So when WHERE runs, the rank doesn't exist yet, and WHERE RANK() OVER (…) = 1 is an error.

    The fix is two steps: a CTE (or ) works out the rank for every row, and the outer query, which runs afterwards, filters on it like an ordinary . (Some , such as Snowflake and BigQuery, add QUALIFY to filter on a window function directly.)

  2. Writefrom a blank page, then run it

    Return the two highest-paid employees in each department, including ties.

    Show answer

    Same as the solution, with rnk <= 2. RANK keeps ties: if two people shared second place, both would be included. Here Engineering gives Asha and Hana (Ben and Chen tie for third, so they are out), Sales gives Dara and Eli, and Marketing has only Fatima.

    sql
    WITH ranked AS (
      SELECT d.dept_name, e.name, e.salary,
             RANK() OVER (PARTITION BY e.dept_id ORDER BY e.salary DESC) AS rnk
      FROM employees e
      JOIN departments d ON d.dept_id = e.dept_id
    )
    SELECT dept_name, name, salary, rnk
    FROM ranked
    WHERE rnk <= 2
    ORDER BY dept_name, rnk, name;

    5 rows

    dept_namenamesalaryrnk
    EngineeringAsha950001
    EngineeringHana800002
    MarketingFatima520001
    SalesDara600001
    SalesEli450002
  3. Predictsay it before you run it

    What does the solution return if Hana's salary becomes 95000?

    Show answer

    Engineering now has two rows: Asha and Hana both earn 95,000, so both get rank 1. Marketing (Fatima) and Sales (Dara) are unchanged. 4 rows in all.

    The below adds name, so the two Engineering rows always come out in the same order.

    sql
    UPDATE employees SET salary = 95000 WHERE name = 'Hana';
    WITH ranked AS (
      SELECT d.dept_name, e.name, e.salary,
             RANK() OVER (PARTITION BY e.dept_id ORDER BY e.salary DESC) AS rnk
      FROM employees e
      JOIN departments d ON d.dept_id = e.dept_id
    )
    SELECT dept_name, name, salary
    FROM ranked
    WHERE rnk = 1
    ORDER BY dept_name, name;

    4 rows

    dept_namenamesalary
    EngineeringAsha95000
    EngineeringHana95000
    MarketingFatima52000
    SalesDara60000
  4. Edge casesSay what happens with NULLs, ties, duplicates or no rows at all, and fix it.

    Change the query so HR appears with NULL name and salary.

    Show answer

    Start from departments and LEFT JOIN employees, and by d.dept_id (not e.dept_id, which is NULL for HR). HR gets one row with NULL name and salary; it is the only row in its partition, so its rank is 1 and it survives the filter.

    sql
    WITH ranked AS (
      SELECT d.dept_name, e.name, e.salary,
             RANK() OVER (PARTITION BY d.dept_id ORDER BY e.salary DESC) AS rnk
      FROM departments d
      LEFT JOIN employees e ON e.dept_id = d.dept_id
    )
    SELECT dept_name, name, salary
    FROM ranked
    WHERE rnk = 1
    ORDER BY dept_name;

    4 rows

    dept_namenamesalary
    EngineeringAsha95000
    HRNULLNULL
    MarketingFatima52000
    SalesDara60000

Practice box: write and run your answers here

Ctrl/⌘ + Enter runs it. Select part of the query to run only that part.

Step 4 Interview-ready, then prove it

Your interview answer

About 20 to 40 seconds, said out loud.

This is top-N-per-group. I rank employees within each department with RANK partitioned by department and ordered by salary descending, in a CTE, then filter to rank one outside — window functions can't go in WHERE. I use RANK so tied top earners are all returned; if they wanted exactly one per department I'd switch to ROW_NUMBER with a . For "top three salaries" I'd use DENSE_RANK less than or equal to three.

Follow-up questions

How would you return the top 3 salaries in each department?

DENSE_RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) <= 3, so that three different salary amounts are covered, with ties.

Why not dept_id with MAX(salary)?

That gives the top amount per department, but not who earns it. You would have to join back, and that is the alternative solution.

Tick each check when you can do it without notes

A topic is finished when you pass all four checks, not when you have read it. Be strict: if you had to look at the notes, it is not ticked yet.

  • Explain Say what it does and when to use it, in plain words, with no notes.
  • Write Write the query from a blank page, then run it to check.
  • Predict Read a query and say which rows it returns before you run it.
  • Edge cases Say what happens with NULLs, ties, duplicates or no rows at all, and fix it.

0 of 4 checks ticked.

Saved in this browser. Back it up or move it to another device.