Bird
Raised Fist0
SQLquery~20 mins

Recursive CTE for hierarchical data in SQL - Practice Problems & Coding Challenges

Choose your learning style10 modes available
Challenge - 5 Problems
🎖️
Recursive CTE Master
Get all challenges correct to earn this badge!
Test your skills under time pressure!
❓ query_result
intermediate
2:00remaining
Output of Recursive CTE for Employee Hierarchy
Given the following table Employees with columns id, name, and manager_id, what is the output of this recursive CTE query?

WITH RECURSIVE EmployeeHierarchy AS (
  SELECT id, name, manager_id, 1 AS level
  FROM Employees
  WHERE manager_id IS NULL
  UNION ALL
  SELECT e.id, e.name, e.manager_id, eh.level + 1
  FROM Employees e
  JOIN EmployeeHierarchy eh ON e.manager_id = eh.id
)
SELECT id, name, level FROM EmployeeHierarchy ORDER BY level, id;
SQL
WITH RECURSIVE EmployeeHierarchy AS (
  SELECT id, name, manager_id, 1 AS level
  FROM Employees
  WHERE manager_id IS NULL
  UNION ALL
  SELECT e.id, e.name, e.manager_id, eh.level + 1
  FROM Employees e
  JOIN EmployeeHierarchy eh ON e.manager_id = eh.id
)
SELECT id, name, level FROM EmployeeHierarchy ORDER BY level, id;
A[{id: 1, name: 'Alice', level: 1}, {id: 2, name: 'Bob', level: 2}, {id: 3, name: 'Charlie', level: 2}, {id: 4, name: 'Diana', level: 3}]
B[{id: 1, name: 'Alice', level: 1}, {id: 2, name: 'Bob', level: 1}, {id: 3, name: 'Charlie', level: 1}, {id: 4, name: 'Diana', level: 1}]
C[{id: 4, name: 'Diana', level: 3}, {id: 3, name: 'Charlie', level: 2}, {id: 2, name: 'Bob', level: 2}, {id: 1, name: 'Alice', level: 1}]
D[{id: 1, name: 'Alice', level: 1}, {id: 3, name: 'Charlie', level: 2}, {id: 2, name: 'Bob', level: 2}, {id: 4, name: 'Diana', level: 3}]
Attempts:
2 left
💡 Hint
Think about how the recursion starts from the top-level manager and adds employees level by level.
📝 Syntax
intermediate
2:00remaining
Identify the Syntax Error in Recursive CTE
Which option contains a syntax error in the recursive CTE definition for hierarchical data?
SQL
WITH RECURSIVE EmployeeTree AS (
  SELECT id, name, manager_id, 1 AS depth
  FROM Employees
  WHERE manager_id IS NULL
  UNION ALL
  SELECT e.id, e.name, e.manager_id, depth + 1
  FROM Employees e
  JOIN EmployeeTree et ON e.manager_id = et.id
)
SELECT * FROM EmployeeTree;
AThe recursive part uses 'depth + 1' without table alias, causing ambiguity.
BThe base query is missing a WHERE clause to filter top-level managers.
CThe recursive CTE is missing the RECURSIVE keyword after WITH.
DThe JOIN condition uses 'e.manager_id = et.id' which is invalid.
Attempts:
2 left
💡 Hint
Check if all columns in the recursive part are properly qualified with table aliases.
❓ optimization
advanced
3:00remaining
Optimize Recursive CTE to Avoid Duplicate Rows
You have a recursive CTE that sometimes returns duplicate rows in the hierarchy. Which option best optimizes the query to avoid duplicates?
SQL
WITH RECURSIVE OrgChart AS (
  SELECT id, name, manager_id, 1 AS level
  FROM Employees
  WHERE manager_id IS NULL
  UNION ALL
  SELECT e.id, e.name, e.manager_id, oc.level + 1
  FROM Employees e
  JOIN OrgChart oc ON e.manager_id = oc.id
)
SELECT * FROM OrgChart;
AAdd DISTINCT in the final SELECT: SELECT DISTINCT * FROM OrgChart;
BAdd a WHERE clause in the recursive part to exclude already visited ids.
CUse a CTE column to track visited ids and filter duplicates in the recursive step.
DReplace JOIN with LEFT JOIN in the recursive part to avoid duplicates.
Attempts:
2 left
💡 Hint
Think about how to track which rows have been processed to prevent revisiting them.
🧠 Conceptual
advanced
1:30remaining
Understanding Recursive CTE Termination Condition
What ensures that a recursive CTE for hierarchical data eventually stops and does not run infinitely?
AThe RECURSIVE keyword automatically limits recursion depth.
BThe base query selects rows with NULL manager_id, providing a starting point.
CThe recursive part must join on a condition that reduces the number of rows each iteration.
DThe recursion stops when no new rows are returned by the recursive SELECT.
Attempts:
2 left
💡 Hint
Consider what happens when the recursive SELECT returns zero rows.
🔧 Debug
expert
3:00remaining
Debugging Unexpected Output in Recursive CTE
A recursive CTE for a category tree returns fewer rows than expected. Which option is the most likely cause?
SQL
WITH RECURSIVE CategoryTree AS (
  SELECT id, name, parent_id
  FROM Categories
  WHERE parent_id IS NULL
  UNION ALL
  SELECT c.id, c.name, c.parent_id
  FROM Categories c
  JOIN CategoryTree ct ON c.parent_id = ct.id
  WHERE c.is_active = TRUE
)
SELECT * FROM CategoryTree;
AThe JOIN condition 'c.parent_id = ct.id' is incorrect and causes missing rows.
BThe WHERE clause 'c.is_active = TRUE' in the recursive part filters out some categories, reducing rows.
CThe base query should include 'c.is_active = TRUE' to match the recursive part.
DThe recursive CTE is missing the RECURSIVE keyword.
Attempts:
2 left
💡 Hint
Check filters in the recursive part that might exclude rows.

Practice

(1/5)
1. What is the main purpose of a RECURSIVE CTE in SQL when working with hierarchical data?
easy
A. To speed up simple SELECT queries without joins
B. To repeatedly query connected rows to explore hierarchical relationships
C. To create temporary tables for storing unrelated data
D. To update multiple tables at once

Solution

  1. Step 1: Understand the role of recursive CTEs

    Recursive CTEs allow querying hierarchical or connected data by repeatedly joining rows to find related entries.
  2. Step 2: Compare options with this purpose

    Only To repeatedly query connected rows to explore hierarchical relationships describes this repeated querying of connected rows to explore hierarchies.
  3. Final Answer:

    To repeatedly query connected rows to explore hierarchical relationships -> Option B
  4. Quick Check:

    Recursive CTE = repeated connected row queries [OK]
Hint: Recursive CTEs repeatedly join related rows to explore hierarchies [OK]
Common Mistakes:
  • Confusing recursive CTEs with simple temporary tables
  • Thinking recursive CTEs speed up unrelated queries
  • Assuming recursive CTEs update data
2. Which of the following is the correct syntax to start a recursive CTE named OrgChart in SQL?
easy
A. DECLARE RECURSIVE OrgChart BEGIN ... END
B. CREATE RECURSIVE TABLE OrgChart AS ( ... )
C. SELECT RECURSIVE OrgChart FROM ...
D. WITH RECURSIVE OrgChart AS ( ... )

Solution

  1. Step 1: Recall recursive CTE syntax

    The correct way to start a recursive CTE is using WITH RECURSIVE Name AS ( ... ).
  2. Step 2: Check each option

    WITH RECURSIVE OrgChart AS ( ... ) matches the correct syntax. Options A, B, and C use invalid or unrelated SQL commands.
  3. Final Answer:

    WITH RECURSIVE OrgChart AS ( ... ) -> Option D
  4. Quick Check:

    Recursive CTE starts with WITH RECURSIVE [OK]
Hint: Recursive CTE always starts with WITH RECURSIVE [OK]
Common Mistakes:
  • Using CREATE instead of WITH for CTEs
  • Trying to SELECT RECURSIVE directly
  • Using DECLARE which is for procedural code
3. Given the table Employees(id, name, manager_id) where manager_id references id, what will the following query return?
WITH RECURSIVE Subordinates AS (
  SELECT id, name, manager_id FROM Employees WHERE id = 2
  UNION ALL
  SELECT e.id, e.name, e.manager_id FROM Employees e
  JOIN Subordinates s ON e.manager_id = s.id
)
SELECT * FROM Subordinates;
medium
A. All employees who report directly or indirectly to employee with id 2
B. Only the employee with id 2
C. All employees who have no manager
D. All employees in the company

Solution

  1. Step 1: Analyze the base query

    The base query selects the employee with id = 2.
  2. Step 2: Analyze the recursive part

    The recursive part joins employees whose manager_id matches any id found so far, thus finding all subordinates under employee 2.
  3. Final Answer:

    All employees who report directly or indirectly to employee with id 2 -> Option A
  4. Quick Check:

    Recursive join finds all subordinates [OK]
Hint: Recursive CTE with join on manager_id finds all subordinates [OK]
Common Mistakes:
  • Thinking it returns only the base employee
  • Assuming it returns employees without managers
  • Believing it returns all employees regardless
4. Identify the error in this recursive CTE that tries to list all descendants of a node in a tree:
WITH RECURSIVE Descendants AS (
  SELECT id, parent_id FROM Tree WHERE id = 1
  UNION ALL
  SELECT t.id, t.parent_id FROM Tree t
  JOIN Descendants d ON t.id = d.parent_id
)
SELECT * FROM Descendants;
medium
A. The JOIN condition should be ON t.parent_id = d.id, not t.id = d.parent_id
B. The base query should select from Descendants, not Tree
C. Recursive CTEs cannot use JOINs
D. The recursive part should use UNION, not UNION ALL

Solution

  1. Step 1: Understand the tree relationship

    Each node's parent_id points to its parent. To find descendants, join where child's parent_id equals parent's id.
  2. Step 2: Check the JOIN condition

    The query incorrectly joins on t.id = d.parent_id. It should be t.parent_id = d.id to find children of current nodes.
  3. Final Answer:

    The JOIN condition should be ON t.parent_id = d.id, not t.id = d.parent_id -> Option A
  4. Quick Check:

    Join child parent_id to parent id [OK]
Hint: Join child's parent_id to parent's id to find descendants [OK]
Common Mistakes:
  • Reversing join condition columns
  • Using base query from recursive CTE itself
  • Thinking recursive CTEs can't use JOIN
  • Confusing UNION and UNION ALL usage
5. You have a parts list table Parts(id, name, parent_part_id) representing assemblies and subassemblies. Write a recursive CTE query to find the full hierarchy of parts under part with id = 10, including the root part itself, and show the level (depth) of each part in the hierarchy starting at 0 for the root. Which query is correct?
hard
A. WITH RECURSIVE PartHierarchy AS ( SELECT id, name, parent_part_id FROM Parts WHERE parent_part_id IS NULL UNION ALL SELECT p.id, p.name, p.parent_part_id FROM Parts p JOIN PartHierarchy ph ON p.id = ph.parent_part_id ) SELECT * FROM PartHierarchy WHERE id = 10;
B. WITH RECURSIVE PartHierarchy AS ( SELECT id, name, parent_part_id, 1 AS level FROM Parts WHERE id = 10 UNION ALL SELECT p.id, p.name, p.parent_part_id, ph.level - 1 FROM Parts p JOIN PartHierarchy ph ON p.id = ph.parent_part_id ) SELECT * FROM PartHierarchy;
C. WITH RECURSIVE PartHierarchy AS ( SELECT id, name, parent_part_id, 0 AS level FROM Parts WHERE id = 10 UNION ALL SELECT p.id, p.name, p.parent_part_id, ph.level + 1 FROM Parts p JOIN PartHierarchy ph ON p.parent_part_id = ph.id ) SELECT * FROM PartHierarchy;
D. WITH RECURSIVE PartHierarchy AS ( SELECT id, name, parent_part_id, 0 AS level FROM Parts WHERE id = 10 UNION ALL SELECT p.id, p.name, p.parent_part_id, ph.level + 1 FROM Parts p JOIN PartHierarchy ph ON p.id = ph.parent_part_id ) SELECT * FROM PartHierarchy;

Solution

  1. Step 1: Identify the base case and level

    The base query selects the root part with id=10 and sets level to 0.
  2. Step 2: Check the recursive join and level increment

    The recursive part joins parts where parent_part_id matches current part's id, and increments level by 1 to track depth.
  3. Step 3: Verify options

    WITH RECURSIVE PartHierarchy AS ( SELECT id, name, parent_part_id, 0 AS level FROM Parts WHERE id = 10 UNION ALL SELECT p.id, p.name, p.parent_part_id, ph.level + 1 FROM Parts p JOIN PartHierarchy ph ON p.parent_part_id = ph.id ) SELECT * FROM PartHierarchy; correctly implements this logic. WITH RECURSIVE PartHierarchy AS ( SELECT id, name, parent_part_id, 1 AS level FROM Parts WHERE id = 10 UNION ALL SELECT p.id, p.name, p.parent_part_id, ph.level - 1 FROM Parts p JOIN PartHierarchy ph ON p.id = ph.parent_part_id ) SELECT * FROM PartHierarchy; has wrong join direction and decrements level. WITH RECURSIVE PartHierarchy AS ( SELECT id, name, parent_part_id FROM Parts WHERE parent_part_id IS NULL UNION ALL SELECT p.id, p.name, p.parent_part_id FROM Parts p JOIN PartHierarchy ph ON p.id = ph.parent_part_id ) SELECT * FROM PartHierarchy WHERE id = 10; starts from root parts but filters later, missing the correct root. WITH RECURSIVE PartHierarchy AS ( SELECT id, name, parent_part_id, 0 AS level FROM Parts WHERE id = 10 UNION ALL SELECT p.id, p.name, p.parent_part_id, ph.level + 1 FROM Parts p JOIN PartHierarchy ph ON p.id = ph.parent_part_id ) SELECT * FROM PartHierarchy; has wrong join direction.
  4. Final Answer:

    The query with base WHERE id=10, level 0, JOIN p.parent_part_id=ph.id, level+1 -> Option C
  5. Quick Check:

    Join on child.parent_part_id = parent.id, level + 1 [OK]
Hint: Join child.parent_part_id = parent.id and increment level [OK]
Common Mistakes:
  • Reversing join condition (parent to child)
  • Starting level at 1 or decrementing level
  • Filtering after recursion instead of base case
  • Using wrong root selection
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard