runtime
47 / 88

lesson 47 Advanced product analysis · advanced

Recursive CTEs

Repeat a query step to explore a hierarchy.

the idea

A recursive CTE has an anchor query that returns the starting rows and a recursive query that joins the CTE to the table to find the next level. SQLite repeats the recursive step until it adds no rows. Carry a depth column to know how far each row is from the start.

Dataset: Developer Platform

The tables the challenges use, with their first 6 rows. Under each column name: the kind of value it holds, and which table it links to.

teams4 columns · 324 rows
team_idnumber · unique idorganization_idnumber · links to organizationsteam_nametextparent_team_idnumber · links to teams
11IdentityNULL
21SecurityNULL
31MessagingNULL
41Observability1
51Data4
61Storage4
team_idnumber · unique id
1
organization_idnumber · links to organizations
1
team_nametext
Identity
parent_team_idnumber · links to teams
NULL
team_idnumber · unique id
2
organization_idnumber · links to organizations
1
team_nametext
Security
parent_team_idnumber · links to teams
NULL
team_idnumber · unique id
3
organization_idnumber · links to organizations
1
team_nametext
Messaging
parent_team_idnumber · links to teams
NULL
team_idnumber · unique id
4
organization_idnumber · links to organizations
1
team_nametext
Observability
parent_team_idnumber · links to teams
1
team_idnumber · unique id
5
organization_idnumber · links to organizations
1
team_nametext
Data
parent_team_idnumber · links to teams
4
team_idnumber · unique id
6
organization_idnumber · links to organizations
1
team_nametext
Storage
parent_team_idnumber · links to teams
4

first 6 of 324 rows · your queries use all of them

developers9 columns · 2,891 rows
developer_idnumber · unique idorganization_idnumber · links to organizationsteam_idnumber · links to teamsusernametextdisplay_nametextroletextmanager_idnumber · links to developersjoined_atdate & timeleft_attext
11NULLzgreen1Zara GreenvpNULL2022-06-03 00:00:00NULL
211dturner2Daniela Turnerdirector12022-07-03 00:00:00NULL
312cthomas3Carlos Thomasdirector12022-11-30 00:00:00NULL
413mkang4Miguel Kangdirector12022-09-29 00:00:00NULL
514rnguyen5Ryan Nguyenmanager22023-07-27 00:00:00NULL
615jabbott6Jose Abbottmanager22022-10-15 00:00:00NULL
developer_idnumber · unique id
1
organization_idnumber · links to organizations
1
team_idnumber · links to teams
NULL
usernametext
zgreen1
display_nametext
Zara Green
roletext
vp
manager_idnumber · links to developers
NULL
joined_atdate & time
2022-06-03 00:00:00
left_attext
NULL
developer_idnumber · unique id
2
organization_idnumber · links to organizations
1
team_idnumber · links to teams
1
usernametext
dturner2
display_nametext
Daniela Turner
roletext
director
manager_idnumber · links to developers
1
joined_atdate & time
2022-07-03 00:00:00
left_attext
NULL
developer_idnumber · unique id
3
organization_idnumber · links to organizations
1
team_idnumber · links to teams
2
usernametext
cthomas3
display_nametext
Carlos Thomas
roletext
director
manager_idnumber · links to developers
1
joined_atdate & time
2022-11-30 00:00:00
left_attext
NULL
developer_idnumber · unique id
4
organization_idnumber · links to organizations
1
team_idnumber · links to teams
3
usernametext
mkang4
display_nametext
Miguel Kang
roletext
director
manager_idnumber · links to developers
1
joined_atdate & time
2022-09-29 00:00:00
left_attext
NULL
developer_idnumber · unique id
5
organization_idnumber · links to organizations
1
team_idnumber · links to teams
4
usernametext
rnguyen5
display_nametext
Ryan Nguyen
roletext
manager
manager_idnumber · links to developers
2
joined_atdate & time
2023-07-27 00:00:00
left_attext
NULL
developer_idnumber · unique id
6
organization_idnumber · links to organizations
1
team_idnumber · links to teams
5
usernametext
jabbott6
display_nametext
Jose Abbott
roletext
manager
manager_idnumber · links to developers
2
joined_atdate & time
2022-10-15 00:00:00
left_attext
NULL

first 6 of 2,891 rows · your queries use all of them

worked example

Run these steps in order to see the idea before you write your own query. The step marked “the pattern” is what the challenges ask for. Running the example does not count toward progress.

  1. Count from 1 to 5 with recursion. Run this. A recursive CTE starts at 1 and keeps adding rows until the condition fails—the pattern you reuse for org trees.

    step-1.sql
    WITH RECURSIVE n(x) AS (
      SELECT 1
      UNION ALL
      SELECT x + 1
      FROM n
      WHERE x < 5
    )
    SELECT x
    FROM n;
  2. See how teams nest. Run this. parent_team_id links each team to its parent; NULL means a top-level team in organization 2.

    step-2.sql
    SELECT team_id, team_name, parent_team_id
    FROM teams
    WHERE organization_id = 2
    ORDER BY parent_team_id NULLS FIRST, team_id;
  3. Put it together. The lesson's pattern in one query.the pattern

    example.sql
    WITH RECURSIVE n(x) AS (
      SELECT 1
      UNION ALL
      SELECT x + 1
      FROM n
      WHERE x < 5
    )
    SELECT x
    FROM n;

common mistake

Joining in the wrong direction (parent to child versus child to parent) walks up the tree instead of down, or stops after one level.

challenge 1

For organization 2, return team_id, team_name and depth for every team: top-level teams (no parent) have depth 0 and each nested level adds 1.

what to return

Columns
team_idteam_namedepth in this order
Rows
any order
challenge-1.sql
 
To master this lesson, solve both challenges without hints and pass the concept check. 3 of 3 to go.
Recursive CTEs — Advanced product analysis · Runtime