Brightlane's product catalog stores categories in a two-level hierarchy. Root categories have no parent on record.
Write a query to return the ID, name, and depth level for every category in the hierarchy.
Assumptions:
- The
categoriestable has one row per category with anid, aname, and aparent_id. - A root category has a missing
parent_id. A subcategory has aparent_idthat references another category'sid. - Every category appears in the result exactly once. Root categories carry depth
1; subcategories whoseparent_idreferences a root carry depth2.
Output:
- One row per category, with columns
id,name, anddepth.
Schema · ecommerce 5 tables
Run previews · Check grades
Write a query, then run it to see results here.
Worked solution Try it yourself first
WITH RECURSIVE
cat_tree AS (
SELECT
id,
name,
parent_id,
1 AS depth
FROM
categories
WHERE
parent_id IS NULL
UNION ALL
SELECT
c.id,
c.name,
c.parent_id,
ct.depth + 1
FROM
categories c
JOIN cat_tree ct ON c.parent_id = ct.id
)
SELECT
id,
name,
depth
FROM
cat_tree The shape
The anchor seeds the recursion with the root categories — the ones whose parent_id is missing — and the recursive member joins each child category to a row already inside the CTE on parent_id = id. One pass adds the subcategories, and a second pass finds no further matches, so the recursion stops.
Clause by clause
- The anchor selects the roots and stamps them at depth
1:
SELECT id, name, parent_id, 1 AS depth
FROM categories
WHERE parent_id IS NULLElectronics, Clothing, Home & Garden, and Books are the four root categories. Each lands in cat_tree carrying depth = 1.
- The recursive member adds one level by joining children to parents already in the CTE:
UNION ALL
SELECT c.id, c.name, c.parent_id, ct.depth + 1
FROM categories c
JOIN cat_tree ct ON c.parent_id = ct.idThe join condition c.parent_id = ct.id is the link from child to parent. The recursive pass matches Phones, Laptops, Tablets, Men's, Women's, Kids', Furniture, and Outdoor to their respective roots and stamps each with ct.depth + 1, which resolves to 2. A second pass finds no rows whose parent_id matches any id newly added at depth 2, so the recursion terminates.
- The final
SELECTreads the accumulated tree:
SELECT id, name, depth
FROM cat_treeEvery category from both passes is in cat_tree by now, each carrying the depth its pass stamped onto it.
The trap
The join direction in the recursive member is what controls traversal direction. c.parent_id = ct.id walks downward: each new row is a child of a row already in the CTE. Flipping the equality to c.id = ct.parent_id would walk upward instead, which is the wrong direction for a root-to-leaf traversal and would never expand past the anchor.
You practiced a two-step WITH RECURSIVE traversal — anchor on root records (where parent_id is missing), recurse one level by linking child to parent.