How does a recursive CTE work, and what are the anchor and the recursive part?
A recursive CTE is a query that refers to itself. It has two parts. The anchor is an ordinary SELECT that produces the starting rows. The recursive part is a SELECT that joins the CTE's own name back to the anchor's result, producing the next level, and the engine repeats it until no new rows appear.
CREATE TABLE staff (
id INTEGER PRIMARY KEY,
name VARCHAR(120) NOT NULL,
manager_id INTEGER
);
INSERT INTO staff (id, name, manager_id) VALUES
(1, 'Ada Lovelace', NULL),
(2, 'Grace Hopper', 1),
(3, 'Alan Turing', 1),
(4, 'Katherine Johnson', 2);
WITH RECURSIVE chain (id, name, manager_id, depth) AS (
SELECT id, name, manager_id, 0
FROM staff
WHERE manager_id IS NULL
UNION ALL
SELECT s.id, s.name, s.manager_id, c.depth + 1
FROM staff AS s
JOIN chain AS c ON s.manager_id = c.id
)
SELECT depth, name
FROM chain
ORDER BY depth, name;Four rows come back: Ada Lovelace at depth 0, Alan Turing and Grace Hopper at depth 1, and Katherine Johnson at depth 2. The anchor found the one person with no manager, and each pass of the recursive part found the next layer down.
The mechanics are worth being precise about. The column list in parentheses after the CTE name — (id, name, manager_id, depth) — declares the shape of the result, and it has to match the anchor's column count and types. The anchor runs once. The recursive part runs repeatedly, with the result of the previous iteration on the right-hand side of the join, and the engine stops when an iteration produces no new rows.
UNION ALL is the usual choice rather than UNION, because UNION would deduplicate each level against the accumulated result — which stops a cycle from looping forever but also removes legitimate duplicate rows and makes the query far slower. If you use UNION to guard against cycles, you need a cycle-detection column in the result, because the deduplication only works if the rows are genuinely distinguishable.
The depth column is not required by the engine, but it is the most useful thing you can add. It gives you the level, it lets you stop the recursion at a maximum depth, and it makes the output order obvious.