I don’t like recursive queries because they’re too rare in the wild and their syntax is not intuitive. To be fair, this section is more for my own self-learning.
How recursive CTEs traverse hierarchies
19.1 Qualify events based on first event
In 2024, Erika Pullum shared a great SQL brainteaser on BlueSky. Given a list of dates, the first date qualifies as TRUE and all subsequent dates qualify if it’s been more than 90 days since the last one. This requires a recursive CTE to solve because the 90 day gaps depend on the first row in the dataset.
Erika Pullum posted a great SQL teaser - I’m not sure what’s the use case but it’s a great way to apply recursive CTEs.
library(duckdb)
Warning: package 'duckdb' was built under R version 4.4.3
Loading required package: DBI
con <-dbConnect(duckdb::duckdb(), ":memory:")dbSendStatement(con, "create table events as select '2024-06-10'::date as d UNION ALL select '2024-08-20'::date as d UNION ALL select '2024-08-22'::date as d UNION ALL select '2024-09-17'::date as d UNION ALL select '2024-09-19'::date as d UNION ALL select '2024-11-01'::date as d UNION ALL select '2024-12-11'::date as d UNION ALL select '2024-12-21'::date as d ")
<duckdb_result f1ce0 connection=da990 statement='create table events as
select '2024-06-10'::date as d
UNION ALL
select '2024-08-20'::date as d
UNION ALL
select '2024-08-22'::date as d
UNION ALL
select '2024-09-17'::date as d
UNION ALL
select '2024-09-19'::date as d
UNION ALL
select '2024-11-01'::date as d
UNION ALL
select '2024-12-11'::date as d
UNION ALL
select '2024-12-21'::date as d
'>
with recursive recursive_cte as (selectmin(d) as d,TRUEas is_after_cooldownfromeventsunionallselectmin(events.d) as d,TRUEas is_after_cooldownfromeventsinnerjoin (selectmax(d) as d from recursive_cte) as ronevents.d > r.d +interval90day)select*from recursive_cte
with recursive recursive_cte as (selectmin(events.d) as dfromeventsunionallSELECTmin(recursive_events.d) as dfromevents recursive_eventsinnerjoin (selectmax(d) as d from recursive_cte) latest_dateon recursive_events.d > latest_date.d +90havingmin(recursive_events.d) isnotnull)selectevents.d,casewhen recursive_cte.d isnotnullthentrueelsefalseendas is_after_cooldownfromeventsleftjoin recursive_cte onevents.d = recursive_cte.dorderbyevents.d