Recursive SQL
March 2022
This week I encountered SQL’s recursive query syntax1 for the first time,
using the WITH RECURSIVE common table expression syntax.
Given an sample hierarchy of book genres we can build up a table of breadcumbs:
| id | name | parent_id | 
|---|---|---|
| 1 | Science Fiction | NULL | 
| 2 | Dystopian | 1 | 
| 3 | Cyberpunk | 2 | 
| 4 | Space opera | 1 | 
WITH RECURSIVE linages AS (
  -- The non-recursive base case, top-level parents only
  SELECT
    ARRAY[genres.name] AS genre_names,
    genres.id AS tail_id
  FROM genres
  WHERE genres.parent_id IS NULL
  UNION ALL
  -- Recursively join sub-genres to their parent
  SELECT
    linages.genre_names || genres.name AS genre_names,
    genres.id AS tail_id
  FROM genres
  INNER JOIN linages ON genres.parent_id = linages.tail_id
)
SELECT ARRAY_TO_STRING(linages.genre_names, ' → ') AS breadcumb
FROM linages;
The result contains every generation of the recursion.
| breadcumb | 
|---|
| Science Fiction | 
| Science Fiction → Space opera | 
| Science Fiction → Dystopian | 
| Science Fiction → Dystopian → Cyberpunk | 
Early in my career I saw SQL as something to avoid or abstract away, but with time I’ve come to love munging data using SQL.