Skip to the solution
MediumDSA12 min read

Recursive CTE for an org chart — how do you list all reports (direct + indirect) of a manager?

86 views

The problem

Question presented to candidate: "We store employees in one table with a manager_id column. Given a manager, write a query that returns everyone who reports to them, directly or through other managers, with how many levels down each person is."

What a strong answer should cover:

  • Why a plain self join is not enough: each join adds exactly one level, and the depth of the tree is unknown
  • A recursive CTE: anchor = the direct reports (manager_id = :id), recursive member joins employees.manager_id = reports.id, carrying a level column
  • Deciding whether the manager themself is in the result (anchor on id = :id with level 0, or on manager_id = :id with level 1)
  • Presentation: a path column to print the chart in tree order, and aggregating the walk for headcount per manager
  • Performance: an index on manager_id turns each iteration into index lookups (PostgreSQL shows a Nested Loop with an Index Scan per working-table row)
  • Robustness: a depth guard or cycle check when the data could contain a loop; alternatives for very read-heavy trees (closure table, materialized path)

Clarifying questions expected:

  • Should the result include the manager, and do you want the depth or the full chain for each person?
  • Can the data contain cycles or people with several managers (a graph, not a tree)?
  • How big is the org and how often is this run: per request, or for a report?

Code / implementation expected: Yes — a recursive CTE listing all reports with their level, plus ideally the tree-ordered view or the headcount roll-up.

Schema used by the examples:

  • recursive-cte
  • hierarchy
  • org-chart

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

12 min read · 12 sections

Run the code

Edit the query and run it against the tables for this question.

SQLAll direct and indirect reports of Raj (id 2), with their level
SQLThe same walk printed as an indented org chart, in tree order
SQLHeadcount roll-up: total and direct reports for every manager
PostgreSQL plan: each iteration uses the index on manager_id
SET enable_seqscan = off;
EXPLAIN (COSTS OFF)
WITH RECURSIVE reports(id, name, level) AS (
  SELECT id, name, 1 FROM employees WHERE manager_id = 2
  UNION ALL
  SELECT e.id, e.name, r.level + 1
  FROM employees AS e
  JOIN reports AS r ON e.manager_id = r.id
)
SELECT name, level FROM reports;

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 63 of 82 in SQL

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All SQL questions
Up nextMedium

What is an index and how does it speed up queries?

Continue