A Recursive CTE (Common Table Expression) is a query that refers to itself to repeatedly process hierarchical or tree-structured data until a condition is met.
Click to reveal answer
beginner
Why use Recursive CTEs for hierarchical data?
Recursive CTEs let you easily query data with parent-child relationships, like organizational charts or file systems, by walking through levels step-by-step.
Click to reveal answer
intermediate
What are the two main parts of a Recursive CTE?
1. Anchor member: The starting query that selects the root rows. 2. Recursive member: The query that references the CTE itself to find child rows.
Click to reveal answer
intermediate
How do you stop infinite recursion in a Recursive CTE?
By including a condition in the recursive member that eventually stops returning new rows, such as when no more child rows exist.
Click to reveal answer
intermediate
Example: What does this Recursive CTE do?
WITH RECURSIVE subordinates AS ( SELECT employee_id, manager_id, 1 AS level FROM employees WHERE manager_id IS NULL UNION ALL SELECT e.employee_id, e.manager_id, s.level + 1 FROM employees e JOIN subordinates s ON e.manager_id = s.employee_id ) SELECT * FROM subordinates;
It finds all employees in a company starting from top managers (no manager) and lists their subordinates at each level, showing the hierarchy depth.
Click to reveal answer
What keyword starts a Recursive CTE in PostgreSQL?
AWITH RECURSIVE
BSTART RECURSIVE
CRECURSIVE CTE
DBEGIN RECURSION
✗ Incorrect
Recursive CTEs always start with the keyword 'WITH RECURSIVE' in PostgreSQL.
In a Recursive CTE, what does the anchor member do?
ALimits the number of recursion steps
BJoins tables together
CDefines the base rows to start recursion
DDeletes rows from the table
✗ Incorrect
The anchor member selects the initial rows from which recursion begins.
How do you prevent infinite loops in Recursive CTEs?
ABy adding a termination condition in the recursive member
BBy using LIMIT 1
CBy running the query only once
DBy disabling recursion in settings
✗ Incorrect
A termination condition stops recursion when no new rows are found.
Which of these is a common use case for Recursive CTEs?
ASorting numbers in ascending order
BUpdating multiple tables
CCalculating averages
DQuerying hierarchical data like organizational charts
✗ Incorrect
Recursive CTEs are ideal for hierarchical data such as trees or organizational structures.
What does the UNION ALL do in a Recursive CTE?
ARemoves duplicate rows
BCombines anchor and recursive member results
CEnds the recursion
DFilters rows by condition
✗ Incorrect
UNION ALL combines the initial anchor rows with the recursive results at each step.
Explain how a Recursive CTE works to retrieve hierarchical data.
Think about starting from the top and walking down the tree.
You got /4 concepts.
Describe a real-life example where Recursive CTEs can be used and why.
Imagine a company with managers and employees.
You got /4 concepts.
Practice
(1/5)
1. What is the main purpose of using a WITH RECURSIVE clause in PostgreSQL when working with hierarchical data?
easy
A. To repeatedly query related rows and build a hierarchy step-by-step
B. To create temporary tables for faster queries
C. To join two unrelated tables efficiently
D. To update multiple rows in a single statement
Solution
Step 1: Understand the purpose of recursive CTEs
Recursive CTEs allow querying hierarchical data by starting with a base case and repeatedly adding related rows.
Step 2: Compare options with this purpose
Only To repeatedly query related rows and build a hierarchy step-by-step describes this step-by-step building of hierarchy, while others describe unrelated SQL features.
Final Answer:
To repeatedly query related rows and build a hierarchy step-by-step -> Option A
Quick Check:
Recursive CTE = step-by-step hierarchy building [OK]
Hint: Recursive CTEs build hierarchies by repeating queries [OK]
Common Mistakes:
Confusing recursive CTE with temporary tables
Thinking recursive CTEs update data
Assuming recursive CTEs join unrelated tables
2. Which of the following is the correct basic syntax to start a recursive CTE named tree in PostgreSQL?
easy
A. RECURSIVE WITH tree AS (SELECT ... UNION ALL SELECT ...)
B. WITH RECURSIVE tree AS (SELECT ... UNION ALL SELECT ...)
C. WITH tree RECURSIVE AS (SELECT ... UNION ALL SELECT ...)
D. WITH tree AS RECURSIVE (SELECT ... UNION ALL SELECT ...)
Solution
Step 1: Recall correct recursive CTE syntax
The correct syntax starts with WITH RECURSIVE cte_name AS ( ... ).
Step 2: Match options to syntax
Only WITH RECURSIVE tree AS (SELECT ... UNION ALL SELECT ...) matches this exact syntax; others misplace the keyword RECURSIVE.
Final Answer:
WITH RECURSIVE tree AS (SELECT ... UNION ALL SELECT ...) -> Option B
Quick Check:
WITH RECURSIVE before CTE name [OK]
Hint: Start with 'WITH RECURSIVE cte_name AS' [OK]
Common Mistakes:
Placing RECURSIVE after CTE name
Using RECURSIVE inside parentheses
Omitting RECURSIVE keyword
3. Given the table employees(id, name, manager_id) with data: 1, 'Alice', NULL 2, 'Bob', 1 3, 'Carol', 2 What will the following query return?
WITH RECURSIVE emp_tree AS (SELECT id, name, manager_id FROM employees WHERE id = 1 UNION ALL SELECT e.id, e.name, e.manager_id FROM employees e JOIN emp_tree et ON e.manager_id = et.id) SELECT name FROM emp_tree ORDER BY id;
medium
A. Alice only
B. Carol only
C. Bob, Carol only
D. Alice, Bob, Carol
Solution
Step 1: Understand base case and recursion
The base case selects employee with id=1 (Alice). The recursive part adds employees whose manager_id matches ids in emp_tree.
Step 2: Trace recursion
First iteration adds Bob (manager_id=1), second adds Carol (manager_id=2). So all three are included.
Final Answer:
Alice, Bob, Carol -> Option D
Quick Check:
Recursive CTE collects all subordinates [OK]
Hint: Recursive CTE collects all descendants starting from base [OK]
Common Mistakes:
Ignoring base case rows
Confusing manager_id direction
Assuming only direct reports are included
4. Consider this recursive CTE:
WITH RECURSIVE cte AS (SELECT id, parent_id FROM items WHERE parent_id IS NULL UNION ALL SELECT i.id, i.parent_id FROM items i JOIN cte ON i.parent_id = cte.id) SELECT * FROM cte;
It returns no rows. What is the most likely cause?
medium
A. The base case SELECT returns no rows because no parent_id is NULL
B. The JOIN condition is reversed and should be cte.parent_id = i.id
C. The recursive part uses UNION instead of UNION ALL
D. The CTE is missing an ORDER BY clause
Solution
Step 1: Check base case condition
The base case selects rows where parent_id IS NULL. If none exist, base returns zero rows.
Step 2: Understand recursion depends on base
Without base rows, recursion never starts, so no rows returned.
Final Answer:
The base case SELECT returns no rows because no parent_id is NULL -> Option A
Quick Check:
Base case must return rows to start recursion [OK]
Hint: Check base case returns rows before recursion [OK]
Common Mistakes:
Assuming JOIN condition is wrong without checking data
Thinking UNION vs UNION ALL causes no rows
Believing ORDER BY affects recursion output
5. You have a table folders(id, name, parent_id) representing nested folders. Write a recursive CTE query to list all folders with their full path from the root, separated by '/'. Which query correctly achieves this?
hard
A. WITH RECURSIVE folder_paths AS (SELECT id, name, parent_id, name AS path FROM folders WHERE parent_id IS NOT NULL UNION ALL SELECT f.id, f.name, f.parent_id, fp.path || '/' || f.name FROM folders f JOIN folder_paths fp ON f.parent_id = fp.id) SELECT id, path FROM folder_paths ORDER BY path;
B. WITH RECURSIVE folder_paths AS (SELECT id, name, parent_id, '/' || name AS path FROM folders WHERE parent_id IS NULL UNION ALL SELECT f.id, f.name, f.parent_id, '/' || fp.path || '/' || f.name FROM folders f JOIN folder_paths fp ON f.parent_id = fp.id) SELECT id, path FROM folder_paths ORDER BY path;
C. WITH RECURSIVE folder_paths AS (SELECT id, name, parent_id, name AS path FROM folders WHERE parent_id IS NULL UNION ALL SELECT f.id, f.name, f.parent_id, fp.path || '/' || f.name FROM folders f JOIN folder_paths fp ON f.parent_id = fp.id) SELECT id, path FROM folder_paths ORDER BY path;
D. WITH RECURSIVE folder_paths AS (SELECT id, name, parent_id, name AS path FROM folders WHERE parent_id IS NULL UNION ALL SELECT f.id, f.name, f.parent_id, fp.path + '/' + f.name FROM folders f JOIN folder_paths fp ON f.parent_id = fp.id) SELECT id, path FROM folder_paths ORDER BY path;
Solution
Step 1: Identify correct base case and path initialization
The base case must select root folders (parent_id IS NULL) and set path to just the folder name without leading '/'. The recursive part builds path as parent_path || '/' || name.
Step 2: Compare options with correct logic
The correct query uses parent_id IS NULL for base, name AS path, and fp.path || '/' || f.name for recursion (PostgreSQL string concat). Distractors fail by using invalid + concat, adding extra '/' (causing '//'), or starting from non-roots (parent_id IS NOT NULL).
Final Answer:
WITH RECURSIVE folder_paths AS (SELECT id, name, parent_id, name AS path FROM folders WHERE parent_id IS NULL UNION ALL SELECT f.id, f.name, f.parent_id, fp.path || '/' || f.name FROM folders f JOIN folder_paths fp ON f.parent_id = fp.id) SELECT id, path FROM folder_paths ORDER BY path; -> Option C
Quick Check:
Use base root rows + '||' concat for paths [OK]
Hint: Use '||' for string concat and start from root folders [OK]
Common Mistakes:
Using '+' instead of '||' for string concatenation
Starting recursion from non-root folders
Adding extra '/' at path start
Recommended
Ready to prove it?
Pass the test and this pattern moves unknownstrongwhile earning +1 Grip toward your certificate.