Recursion
Common Table Expressions allow us to, effectively, create our own temporarytables for the duration of a query - they’re largely a convenience to help usmake more readable SQL. Using the WITH RECURSIVE modifier, however, it’spossible for us to create recursive queries. This is enormously advantageousfor working with tree and graph-structured data - imagine retrieving all of therelations of a graph node to a given depth, for example.
Find the upward recommendation chain for member ID 27
Find the upward recommendation chain for member ID 27: that is, the memberwho recommended them, and the member who recommended that member, and so on.Return member ID, first name, and surname. Order by descending member id.
- WITH RECURSIVE recommenders(recommender) as (
- SELECT recommendedby FROM members WHERE memid = 27
- UNION ALL
- SELECT mems.recommendedby
- FROM recommenders recs
- INNER JOIN members AS mems ON mems.memid = recs.recommender
- )
- SELECT recs.recommender, mems.firstname, mems.surname
- FROM recommenders AS recs
- INNER JOIN members AS mems ON recs.recommender = mems.memid
- ORDER By memid DESC;
- # Base-case of recursive CTE. Get member recommender where memid=27.
- base = (Member
- .select(Member.recommendedby)
- .where(Member.memid == 27)
- .cte('recommenders', recursive=True, columns=('recommender',)))
- # Recursive term of CTE. Get recommender of previous recommender.
- MA = Member.alias()
- recursive = (MA
- .select(MA.recommendedby)
- .join(base, on=(MA.memid == base.c.recommender)))
- # Combine the base-case with the recursive term.
- cte = base.union_all(recursive)
- # Select from the recursive CTE, joining on member to get name info.
- query = (cte
- .select_from(cte.c.recommender, Member.firstname, Member.surname)
- .join(Member, on=(cte.c.recommender == Member.memid))
- .order_by(Member.memid.desc()))