You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ClickHouse中查询节点间的所有可能路径?

ClickHouse中实现节点全路径遍历的可行方案

由于ClickHouse不支持标准递归CTE,我们可以通过多轮JOIN迭代、数组函数模拟或预计算存储的方式实现需求,以下是具体可行方案:

方案一:多轮JOIN迭代拼接路径

适合路径层级有限的场景,通过逐层JOIN拼接路径:

步骤1:获取初始路径(长度为1的边)

SELECT
    concat(from, ',', to) AS paths,
    toString(id) AS ids
FROM nodes

步骤2:迭代拼接深层路径

每一轮JOIN都以上一轮路径的终点作为起点,拼接下一层节点:

-- 二级路径(a→b→d)
SELECT
    concat(n1.paths, ',', n2.to) AS paths,
    concat(n1.ids, ',', toString(n2.id)) AS ids
FROM (
    SELECT concat(from, ',', to) AS paths, to AS current_node, toString(id) AS ids FROM nodes
) n1
JOIN nodes n2 ON n1.current_node = n2.from

-- 三级路径(a→b→d→e)
SELECT
    concat(n1.paths, ',', n2.to) AS paths,
    concat(n1.ids, ',', toString(n2.id)) AS ids
FROM (
    SELECT concat(n1.paths, ',', n2.to) AS paths, n2.to AS current_node, concat(n1.ids, ',', toString(n2.id)) AS ids
    FROM (SELECT concat(from, ',', to) AS paths, to AS current_node, toString(id) AS ids FROM nodes) n1
    JOIN nodes n2 ON n1.current_node = n2.from
) n1
JOIN nodes n2 ON n1.current_node = n2.from

步骤3:合并所有层级结果

用UNION ALL将各层级路径合并,得到最终结果:

-- 初始路径
SELECT concat(from, ',', to) AS paths, toString(id) AS ids FROM nodes

UNION ALL

-- 二级路径
SELECT concat(n1.paths, ',', n2.to) AS paths, concat(n1.ids, ',', toString(n2.id)) AS ids
FROM (SELECT concat(from, ',', to) AS paths, to AS current_node, toString(id) AS ids FROM nodes) n1
JOIN nodes n2 ON n1.current_node = n2.from

UNION ALL

-- 三级路径
SELECT concat(n1.paths, ',', n2.to) AS paths, concat(n1.ids, ',', toString(n2.id)) AS ids
FROM (
    SELECT concat(n1.paths, ',', n2.to) AS paths, n2.to AS current_node, concat(n1.ids, ',', toString(n2.id)) AS ids
    FROM (SELECT concat(from, ',', to) AS paths, to AS current_node, toString(id) AS ids FROM nodes) n1
    JOIN nodes n2 ON n1.current_node = n2.from
) n1
JOIN nodes n2 ON n1.current_node = n2.from

方案二:数组函数模拟路径扩展

用数组存储路径节点和ID,结合数组函数实现灵活的路径扩展:

WITH
    -- 初始路径数组
    initial AS (SELECT [from, to] AS path_nodes, [id] AS path_ids FROM nodes),
    -- 扩展二级路径
    level1 AS (
        SELECT
            arrayConcat(n.path_nodes, [m.to]) AS path_nodes,
            arrayConcat(n.path_ids, [m.id]) AS path_ids
        FROM initial n
        JOIN nodes m ON n.path_nodes[-1] = m.from
    ),
    -- 扩展三级路径
    level2 AS (
        SELECT
            arrayConcat(n.path_nodes, [m.to]) AS path_nodes,
            arrayConcat(n.path_ids, [m.id]) AS path_ids
        FROM level1 n
        JOIN nodes m ON n.path_nodes[-1] = m.from
    )

-- 合并所有层级并格式化输出
SELECT
    arrayStringConcat(path_nodes, ',') AS paths,
    arrayStringConcat(arrayMap(x -> toString(x), path_ids), ',') AS ids
FROM initial

UNION ALL

SELECT
    arrayStringConcat(path_nodes, ',') AS paths,
    arrayStringConcat(arrayMap(x -> toString(x), path_ids), ',') AS ids
FROM level1

UNION ALL

SELECT
    arrayStringConcat(path_nodes, ',') AS paths,
    arrayStringConcat(arrayMap(x -> toString(x), path_ids), ',') AS ids
FROM level2

方案三:预计算路径存储(适合静态数据)

如果节点数据更新不频繁,可以通过定时任务预先计算所有路径,将结果写入一张专用表:

-- 创建结果表
CREATE TABLE IF NOT EXISTS node_paths (
    paths String,
    ids String
) ENGINE = MergeTree()
PRIMARY KEY paths;

-- 定期执行路径计算并写入(可通过ClickHouse任务或外部脚本)
INSERT INTO node_paths
-- 此处填入方案一或方案二的完整查询语句

查询时直接读取node_paths表即可,性能最优。

注意事项

  • 若存在循环路径(如a→b→a),需在逻辑中加入循环检测(比如用arrayExists判断当前节点是否已在路径中),避免无限迭代。
  • 对于层级极深的场景,可借助Python UDF实现递归逻辑,或用外部工具(如Spark)预处理路径后导入ClickHouse。

内容的提问来源于stack exchange,提问作者gs8282

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 20:20:35