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

Employees who earn more than their manager

Must knowInterview problemEasy

Find every employee who earns more than their own manager. Show the employee's name and salary, and the manager's name and salary.

Not started

Step 1 Solve it yourself first

Tables you will use

employees

What it really tests

Whether you can treat one as two — a — and read a key the right way round.

The pattern: Self join

"Their manager", "their parent", "the person who referred them" — a pointing at another of the same table.

Picture it

flowchart LR
  H["e: Hana · 80000<br/>manager_id 2"] -->|"m.emp_id = e.manager_id"| B["m: Ben · 70000"]
  B --> C{"80000 more than 70000?"}
  C -->|"yes"| K["keep Hana"]
Each employee row is paired with its manager's row from the same table, and the two salaries are compared side by side.

The approach, step by step

  1. Notice that manager_id points to another row of employees.
  2. Use the table twice: e for the employee, m for the manager.
  3. Join with m.emp_id = e.manager_id (m is e's manager).
  4. Keep rows where e.salary > m.salary. An is fine here: people with no manager can't earn more than one.

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: a self join

sql
SELECT e.name AS employee, e.salary,
       m.name AS manager, m.salary AS manager_salary
FROM employees e
JOIN employees m ON m.emp_id = e.manager_id
WHERE e.salary > m.salary
ORDER BY e.name;

1 row

employeesalarymanagermanager_salary
Hana80000Ben70000

Another way: A

sql
SELECT e.name AS employee
FROM employees e
WHERE e.salary > (
  SELECT m.salary
  FROM employees m
  WHERE m.emp_id = e.manager_id
)
ORDER BY e.name;

1 row

employee
Hana

Works, but can't show the manager's name without another . The self join is clearer.

Edge cases to say out loud

  • Employees with no manager (Asha): the inner join drops them, which is correct here.
  • Equal salaries: "more than" means strictly greater, so a tie is not included — confirm with the interviewer.
  • A manager_id that points to a deleted employee: the inner join drops the row; a should prevent it.

Common mistakes

  • Joining the wrong way round (e.emp_id = m.manager_id), which compares people with their reports.
  • Leaving columns unprefixed, so name and salary are ambiguous.

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 what each stands for and why the condition is written that way round.

    Show answer

    The table is used twice. e is the employee: the row we are asking about. m is the manager: a second copy of the same table, used to look up the boss.

    m.emp_id = e.manager_id reads: "find the row in m whose id is the manager_id written on the employee's row". For Hana, manager_id is 2, so m is Ben's row.

    Written the other way round, e.emp_id = m.manager_id, the roles swap: m becomes someone who reports to e. Then e.salary > m.salary would find managers who earn more than their reports, which is nearly everyone.

  2. Writefrom a blank page, then run it

    Find employees who were hired before their manager.

    Show answer

    The same self join, comparing hire dates instead of salaries. In this data nobody was hired before their manager: Asha (2019) came before everyone she manages, and so did Dara and Ben (2020). An empty result is the right answer here.

    sql
    SELECT e.name AS employee, e.hire_date,
           m.name AS manager, m.hire_date AS manager_hired
    FROM employees e
    JOIN employees m ON m.emp_id = e.manager_id
    WHERE e.hire_date < m.hire_date
    ORDER BY e.name;

    No rows

    employeehire_datemanagermanager_hired
  3. Predictsay it before you run it

    If Eli's salary rose to 61000, which rows would the solution return?

    Show answer

    Two rows: Eli (61,000) over his manager Dara (60,000), and Hana (80,000) over Ben (70,000), as before. Sorted by name, Eli comes first.

    sql
    UPDATE employees SET salary = 61000 WHERE name = 'Eli';
    SELECT e.name AS employee, e.salary,
           m.name AS manager, m.salary AS manager_salary
    FROM employees e
    JOIN employees m ON m.emp_id = e.manager_id
    WHERE e.salary > m.salary
    ORDER BY e.name;

    2 rows

    employeesalarymanagermanager_salary
    Eli61000Dara60000
    Hana80000Ben70000
  4. Edge casesSay what happens with NULLs, ties, duplicates or no rows at all, and fix it.

    Change the solution to also list employees with no manager, showing for the manager columns.

    Show answer

    Use a so employees with no manager keep their row (with NULLs), and let them through the on purpose: e.salary > m.salary is unknown when m is NULL, so add OR m.emp_id IS NULL. Asha now appears with NULL manager columns, next to Hana.

    sql
    SELECT e.name AS employee, e.salary,
           m.name AS manager, m.salary AS manager_salary
    FROM employees e
    LEFT JOIN employees m ON m.emp_id = e.manager_id
    WHERE e.salary > m.salary OR m.emp_id IS NULL
    ORDER BY e.name;

    2 rows

    employeesalarymanagermanager_salary
    Asha95000NULLNULL
    Hana80000Ben70000

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.

manager_id references the same table, so I self-join employees as e and m, matching the manager alias's emp_id to the employee's manager_id, and filter where the employee's salary is greater than the manager's. An inner join is right here because someone without a manager can't out-earn one.

Follow-up questions

How would you include the manager's manager?

Join employees a third time: JOIN employees mm ON mm.emp_id = m.manager_id, as a LEFT JOIN if the chain can end.

How would you count, per manager, how many reports earn more than them?

Same self join and filter, then m.emp_id, m.name with COUNT(*).

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.