✏️ Explanatory Question

Recursive CTEs — traverse a hierarchical org chart to any depth

👁 7 Views
📘 Detailed Answer
🟢 Easy
💡

Answer with Explanation

135

Recursive CTEs — traverse a hierarchical org chart to any depth

Level: Hard — Hierarchical data (org charts, categories, trees) is a classic hard problem; recursive CTEs are the clean solution.

Scenario: Your employees table stores a manager_id pointing to another employee (self-referencing). HR wants "the full reporting chain under the CEO" — every employee, at every level, no matter how deep. A regular JOIN cannot handle arbitrary depth. How do you traverse the tree?

What a Recursive CTE Is (MySQL 8.0+)

A recursive CTE is a WITH RECURSIVE query that references itself, letting it walk a hierarchy level by level. It has two parts joined by UNION ALL: an anchor (the starting rows) and a recursive member (which repeatedly joins back to find the next level).

Sample Data — employees (self-referencing)

emp_idnamemanager_id
1Ajay (CEO)NULL
2Rumman1
3Krushna1
4Swetha2
5Ritesh2
6Manjula4

The Recursive CTE — Full Hierarchy with Levels

WITH RECURSIVE org_chart AS (
    -- ANCHOR: start at the top (the CEO, who has no manager)
    SELECT emp_id, name, manager_id, 1 AS level,
           CAST(name AS CHAR(200)) AS path
    FROM employees
    WHERE manager_id IS NULL

    UNION ALL

    -- RECURSIVE MEMBER: find each employee whose manager is already in the tree
    SELECT e.emp_id, e.name, e.manager_id, oc.level + 1,
           CONCAT(oc.path, ' > ', e.name)
    FROM employees e
    JOIN org_chart oc ON e.manager_id = oc.emp_id
)
SELECT emp_id, name, level, path
FROM org_chart
ORDER BY level, emp_id;
emp_idnamelevelpath
1Ajay (CEO)1Ajay (CEO)
2Rumman2Ajay (CEO) > Rumman
3Krushna2Ajay (CEO) > Krushna
4Swetha3Ajay (CEO) > Rumman > Swetha
5Ritesh3Ajay (CEO) > Rumman > Ritesh
6Manjula4Ajay (CEO) > Rumman > Swetha > Manjula

How it executes: The anchor runs once (the CEO). Then the recursive member runs repeatedly — each pass finds employees reporting to those found in the previous pass — until no new rows appear. The level counter and path string are built up as it descends.

Walking UP the Tree — One Employee's Management Chain

-- Find all managers ABOVE a specific employee (Manjula, id 6)
WITH RECURSIVE mgr_chain AS (
    SELECT emp_id, name, manager_id
    FROM employees WHERE emp_id = 6            -- anchor: the employee

    UNION ALL

    SELECT e.emp_id, e.name, e.manager_id
    FROM employees e
    JOIN mgr_chain mc ON e.emp_id = mc.manager_id  -- climb to the manager
)
SELECT * FROM mgr_chain;
-- Manjula -> Swetha -> Rumman -> Ajay (the chain upward)

Subtree Aggregation — Count All Reports (direct + indirect)

-- How many people report to Rumman, at any depth below him?
WITH RECURSIVE subtree AS (
    SELECT emp_id FROM employees WHERE emp_id = 2      -- start at Rumman
    UNION ALL
    SELECT e.emp_id FROM employees e
    JOIN subtree s ON e.manager_id = s.emp_id
)
SELECT COUNT(*) - 1 AS total_reports FROM subtree;   -- minus Rumman himself

Guard against infinite loops: If the data has a cycle (A manages B, B manages A by mistake), a recursive CTE loops forever. MySQL caps recursion at cte_max_recursion_depth (default 1000) and errors out. For safety on messy data, add a WHERE level < 100 guard or track visited nodes in the path.

Interviewer follow-up: "Recursive CTEs get slow on deep/wide trees — what are the alternatives?" → For read-heavy hierarchies, consider a materialized path (store the full path like /1/2/4/ and query with LIKE '/1/2/%') or the nested set model (store left/right bounds). Both trade write complexity for very fast subtree reads — better than recursion when the tree is queried far more than it changes.