Chapter 14 · Time, History, Hierarchies, and Recursive Structures

Adjacency Lists and Recursive Hierarchies

Represent recursive hierarchies with adjacency lists, understand recursive queries, cycle prevention, root/leaf semantics, and hierarchy integrity.

Beginner65–90 minutesAdjacency lists + recursionLast reviewed: August 2026

Learning outcomes

Many business structures are hierarchical: categories contain subcategories, organizational units contain teams, assets contain subassemblies, and locations contain sublocations. The simplest relational representation is the adjacency list, where each row stores a reference to its parent.

01

Model parent/child hierarchies with self-referencing foreign keys.

02

Query ancestors and descendants recursively.

03

Prevent cycles and invalid parent relationships.

04

Recognize when adjacency lists are sufficient.

Adjacency-list schema

sql · example
FailureCategory(  category_id,  parent_category_id,  code,  display_name)FOREIGN KEY (parent_category_id)REFERENCES FailureCategory(category_id)

Root rows

A root usually has:

model · example
parent_category_id IS NULL

or references a dedicated synthetic root, depending on model requirements.

Example hierarchy

One row per node

Each category stores only its immediate parent. The full path is derived by traversal.

Recursive query

sql · example
WITH RECURSIVE tree AS (  SELECT category_id, parent_category_id, display_name, 0 AS depth  FROM failure_category  WHERE category_id = :root  UNION ALL  SELECT c.category_id, c.parent_category_id, c.display_name, t.depth + 1  FROM failure_category c  JOIN tree t    ON c.parent_category_id = t.category_id)SELECT *FROM tree;

Ancestor traversal

The same technique can walk upward by joining parent rows instead of children.

Index the parent reference

model · example
INDEX(parent_category_id)

supports “find children of this node” efficiently.

Moving a subtree

With adjacency lists, moving a node and all descendants usually requires changing only the moved node's parent reference. This is a major write-side advantage.

Cycle problem

Invalid:

model · example
A parent=BB parent=CC parent=A

The foreign keys are individually valid, yet the hierarchy contains a cycle.

Cycle prevention

Possible strategies include:

  • recursive validation before parent update;
  • triggers;
  • closure-table constraints;
  • application/domain service checks;
  • database-specific cycle detection features.
Hierarchy invariant

A self-referencing foreign key prevents missing parents, not cycles.

Multiple roots

Decide whether multiple independent trees are allowed. If not, enforce a single-root business rule.

Depth limits

Some taxonomies permit only a fixed maximum depth. If this is a business rule, validate it explicitly rather than assuming the UI will prevent deeper nesting.

Sibling uniqueness

You may require category code unique within parent:

model · example
UNIQUE(parent_category_id, code)

NULL/root semantics need careful handling depending on DBMS.

Ordering siblings

model · example
sort_order

belongs on the child row if it controls display order within the parent.

Soft deletion in hierarchies

Deleting a parent raises lifecycle questions:

  • cascade delete descendants?
  • reject while children exist?
  • reparent children?
  • soft-delete the subtree?

Choose based on domain meaning.

WorkshopHub hierarchy candidates

HierarchyAdjacency list fit
Failure categoriesExcellent
Organization teamsGood
Asset assembly treeGood if tree-shaped
Road networkPoor; graph-like

When adjacency list is enough

Use it when:

  • moves are common;
  • depth is modest;
  • recursive queries are supported;
  • ancestor/descendant reads are not extreme hot paths.

Practice: model an organization

Department tree

Design Department with arbitrary nesting and a unique department code among siblings. What columns and constraints are needed?

Review answer

Use department_id PK, parent_department_id nullable FK to Department, department_code, name, optional sort_order, and a scoped uniqueness rule on parent + code. Add cycle-prevention logic because the self-FK alone is insufficient.

Summary and next lesson

Adjacency lists are the simplest relational hierarchy model: easy to understand, easy to move, and compatible with recursive SQL. Their main costs are recursive traversal and cycle enforcement. The next lesson compares alternative hierarchy structures optimized for faster subtree and ancestor queries.

References

  • Joe Celko, Trees and Hierarchies in SQL for Smarties.
  • PostgreSQL documentation on recursive queries.
  • SQL standard recursive common table expression concepts.

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only assets compatible with the Ethereum/ERC-20 network. Do not send TRC-20/TRON assets.