Organization Hierarchy With Subordinate Counts (Recursive CTE)
Return each employee with their full management chain using a recursive CTE.
- Recursive CTE
- CTEs
- Joins
- Subqueries
- Aggregation
Interview brief
Understand the request
Org design analyst The org hierarchy report uses a recursive CTE to walk the management chain from each employee up to the top.
Build the org hierarchy with a recursive CTE. For each employee show their org_level (CEO = 1), the full management_path (CEO -> … -> employee), and total_subordinates (everyone in their reporting chain, direct + indirect). Return employee_id, first_name, last_name, org_level, management_path, total_subordinates — ordered by org_level, employee_id.
Return
- Return employee_name, manager_name, and level (depth in hierarchy).
Constraints
- Use a recursive WITH clause to traverse the self-referencing manager_id relationship.
Data you will use
Review the relevant tables before deciding how to join, filter, or aggregate them.
employees
employee_idINTEGERfirst_nameVARCHAR(50)last_nameVARCHAR(50)manager_idINTEGER
Hints, when you need them
Open one clue at a time so you still do the reasoning.
Hint 1
Recursive CTE: anchor (CEO, level 1) UNION ALL the step that joins each employee to their manager row.
Hint 2
Build management_path by concatenating names as you descend.
Hint 3
Subtree count: a node's descendants are exactly the rows whose path STARTS WITH this node's path (LIKE path || '%'); subtract 1 to exclude self.
Verified SQL answer
Attempt the problem first, then compare structure and reasoning—not just syntax.
Reveal solution and explanation
WITH RECURSIVE org AS (SELECT employee_id, first_name, last_name, manager_id, 1 AS org_level, first_name || ' ' || last_name AS management_path FROM employees WHERE manager_id IS NULL UNION ALL SELECT e.employee_id, e.first_name, e.last_name, e.manager_id, o.org_level + 1, o.management_path || ' -> ' || e.first_name || ' ' || e.last_name FROM employees e INNER JOIN org o ON e.manager_id = o.employee_id), sub AS (SELECT a.employee_id, COUNT(b.employee_id) - 1 AS total_subordinates FROM org a LEFT JOIN org b ON b.management_path LIKE a.management_path || '%' GROUP BY a.employee_id) SELECT o.employee_id, o.first_name, o.last_name, o.org_level, o.management_path, s.total_subordinates FROM org o INNER JOIN sub s ON s.employee_id = o.employee_id ORDER BY o.org_level, o.employee_id;Why this works
Recursive CTEs walk arbitrary-depth hierarchies. The path-prefix LIKE trick is a neat way to count an entire subtree without a second recursion. Note: on MySQL/SQL Server the `||` concatenation must become CONCAT/+.
Expected result
Use this output to verify values, aliases, ordering, and row count.
| employee_id | first_name | last_name | org_level | management_path | total_subordinates |
|---|---|---|---|---|---|
| 1 | Jennifer | King | 1 | Jennifer King | 14 |
| 2 | Michael | Scott | 2 | Jennifer King -> Michael Scott | 5 |
| 3 | Sarah | Johnson | 2 | Jennifer King -> Sarah Johnson | 3 |
| 11 | Patricia | Davis | 2 | Jennifer King -> Patricia Davis | 1 |
| 13 | Jessica | Moore | 2 | Jennifer King -> Jessica Moore | 1 |
| 4 | David | Martinez | 3 | Jennifer King -> Michael Scott -> David Martinez | 0 |
| 5 | Emily | Chen | 3 | Jennifer King -> Michael Scott -> Emily Chen | 0 |
| 6 | Robert | Williams | 3 | Jennifer King -> Michael Scott -> Robert Williams | 0 |
| 7 | Lisa | Anderson | 3 | Jennifer King -> Michael Scott -> Lisa Anderson | 0 |
| 8 | James | Taylor | 3 | Jennifer King -> Sarah Johnson -> James Taylor | 0 |
Previewing 10 of 15 expected rows. Run the query in the editor to inspect the full result.
Learn the concepts behind this answer
Strengthen your understanding with these targeted learning topics: