在duckdb 递归CTE中实现深度优先搜索DFS

AgenticCoding·十二月创作之星挑战赛 10w+人浏览 433人参与

原帖地址 https://github.com/duckdb/duckdb/discussions/15386

通常的递归CTE都是广度优先搜索(BFS)

WITH RECURSIVE 
edges(a, b) as(
VALUES
  (1, 2),
  (1, 3),
  (2, 4),
  (4, 5),
  (4, 6)
), 
bfs(node, path) AS (
  SELECT 1 AS node, [] :: STRUCT("from" INT, "to" INT)[] AS path -- Start with node 1 (root)
  UNION ALL
  SELECT e.b, bft.path|| [{'from': bft.node, 'to': e.b}]  
  FROM   bfs AS bft,
         edges AS e
  WHERE  bft.node = e.a
)
SELECT * FROM bfs;

┌───────┬────────────────────────────────────────────────────────────────────┐
│ node  │                                path                                │
│ int32 │               struct("from" integer, "to" integer)[]               │
├───────┼────────────────────────────────────────────────────────────────────┤
│     1[]                                                                 │
│     2[{'from': 1, 'to': 2}]                                             │
│     3[{'from': 1, 'to': 3}]                                             │
│     4[{'from': 1, 'to': 2}, {'from': 2, 'to': 4}]                       │
│     5[{'from': 1, 'to': 2}, {'from': 2, 'to': 4}, {'from': 4, 'to': 5}] │
│     6[{'from': 1, 'to': 2}, {'from': 2, 'to': 4}, {'from': 4, 'to': 6}] │
└───────┴────────────────────────────────────────────────────────────────────┘

DuckDB CTE模块的设计者kryonix提供了如下技巧提供DFS,但是还有问题,没有求出全部路径。

WITH RECURSIVE 
edges(a, b) as(
VALUES
  (1, 2),
  (1, 3),
  (2, 4),
  (4, 5),
  (4, 6)
), 
dfs(stack, path) AS (
  SELECT [1] AS stack, [] :: STRUCT("from" INT, "to" INT)[] AS path -- Start with node 1 (root)
    UNION ALL
  (WITH siblings AS (
    SELECT ARRAY_AGG(e.b ORDER BY e.b ASC) AS siblings
    --                                ^^^
    -- This determines the order of traversal of the siblings
    FROM   dfs AS dft,
           edges AS e
    WHERE  dft.stack[1] = e.a
  )
  SELECT x.*
  FROM   siblings AS _(s), LATERAL
         (SELECT s || dft.stack[2:] AS stack, -- Push the stack
                 dft.path || [{'from': dft.stack[1], 'to': s[1]}] AS path -- Add the edge to the path
          FROM   dfs AS dft
          WHERE  array_length(s) > 0 -- There are more siblings to traverse
            UNION ALL
          SELECT dft.stack[2:], -- Pop the stack
                 [] :: STRUCT("from" INT, "to" INT)[] AS path -- Reset the path
          FROM   dfs AS dft
          WHERE  array_length(s) IS NULL AND dft.stack <> [] -- No more siblings to traverse
          ) AS x
  )
)
SELECT * FROM dfs;

┌───────────┬────────────────────────────────────────────────────────────────────┐
│   stack   │                                path                                │
│  int32[]  │               struct("from" integer, "to" integer)[]               │
├───────────┼────────────────────────────────────────────────────────────────────┤
│ [1][]                                                                 │
│ [2, 3][{'from': 1, 'to': 2}]                                             │
│ [4, 3][{'from': 1, 'to': 2}, {'from': 2, 'to': 4}]                       │
│ [5, 6, 3][{'from': 1, 'to': 2}, {'from': 2, 'to': 4}, {'from': 4, 'to': 5}] │
│ [6, 3][]                                                                 │
│ [3][]                                                                 │
│ [][]                                                                 │
└───────────┴────────────────────────────────────────────────────────────────────┘
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值