Sql 循环的递归CTE停止条件
我需要使用递归CTE迭代带有循环的图 问题在于循环部分 如果有循环,我想选择最短路径。 这基本上意味着忽略循环,因为递归是“宽度优先” 下面的示例显示了返回的数据: 问题是注释掉的Sql 循环的递归CTE停止条件,sql,postgresql,graph,common-table-expression,recursive-query,Sql,Postgresql,Graph,Common Table Expression,Recursive Query,我需要使用递归CTE迭代带有循环的图 问题在于循环部分 如果有循环,我想选择最短路径。 这基本上意味着忽略循环,因为递归是“宽度优先” 下面的示例显示了返回的数据: 问题是注释掉的INSERT语句创建了一个循环。 显然,如果没有注释,查询将永远不会完成 我需要的是返回与没有循环时相同的数据 DROP TABLE IF EXISTS edges; CREATE TABLE edges( src integer, dst integer, data integer ); INSERT
INSERT
语句创建了一个循环。
显然,如果没有注释,查询将永远不会完成
我需要的是返回与没有循环时相同的数据
DROP TABLE IF EXISTS edges;
CREATE TABLE edges(
src integer,
dst integer,
data integer
);
INSERT INTO edges VALUES (1, 2, 1);
INSERT INTO edges VALUES (2, 3, 1);
--INSERT INTO edges VALUES (3, 2, 1); -- This entry creates a loop
INSERT INTO edges VALUES (1, 4, 1);
INSERT INTO edges VALUES (4, 5, 1);
INSERT INTO edges VALUES (5, 2, 1);
INSERT INTO edges VALUES (1, 4, 2);
INSERT INTO edges VALUES (4, 5, 2);
INSERT INTO edges VALUES (4, 6, 2);
WITH RECURSIVE paths AS (
-- For simplicity assume node 1 is the start
-- we'll have two starting nodes for data = 1 and 2
SELECT DISTINCT
src as node
, data as data
, 0 as depth
, src::text as path
FROM edges
WHERE
src = 1
UNION ALL
SELECT DISTINCT
edges.dst
, edges.data
, depth + 1
, paths.path || '->' || edges.dst::text
FROM paths
JOIN edges ON edges.src = paths.node AND edges.data = paths.data
-- AND eliminate loops?
)
SELECT * FROM paths;
返回:
node | data | depth | path
------+------+-------+---------------
1 | 1 | 0 | 1
1 | 2 | 0 | 1
2 | 1 | 1 | 1->2
4 | 1 | 1 | 1->4
4 | 2 | 1 | 1->4
3 | 1 | 2 | 1->2->3
5 | 2 | 2 | 1->4->5
6 | 2 | 2 | 1->4->6
5 | 1 | 2 | 1->4->5
2 | 1 | 3 | 1->4->5->2
3 | 1 | 4 | 1->4->5->2->3
(11 rows)
这里的条件是而不是paths.path-LIKE“%”| | edges.dst::text | |‘%”
我们避免了会导致循环的后边缘。处理循环的标准方法是在途中构建一个数组,并检查其中是否存在元素:
WITH RECURSIVE paths AS (
SELECT DISTINCT
src as node
, data as data
, 0 as depth
, src::text as path
, false as is_cycle
, ARRAY[src] as path_array
FROM edges
WHERE src IN (1,2)
UNION ALL
SELECT DISTINCT
edges.dst
, edges.data
, depth + 1
, paths.path || '->' || edges.dst::text
, dst = ANY(path_array)
, path_array || dst
FROM paths
JOIN edges
ON edges.src = paths.node
AND edges.data = paths.data
AND NOT is_cycle
)
SELECT * FROM paths;
输出:
+-------+-------+--------+-------------------+-----------+---------------+
| node | data | depth | path | is_cycle | path_array |
+-------+-------+--------+-------------------+-----------+---------------+
| 1 | 1 | 0 | 1 | f | {1} |
| 1 | 2 | 0 | 1 | f | {1} |
| 2 | 1 | 0 | 2 | f | {2} |
| 2 | 1 | 1 | 1->2 | f | {1,2} |
| 3 | 1 | 1 | 2->3 | f | {2,3} |
| 4 | 1 | 1 | 1->4 | f | {1,4} |
| 4 | 2 | 1 | 1->4 | f | {1,4} |
| 2 | 1 | 2 | 2->3->2 | t | {2,3,2} |
| 3 | 1 | 2 | 1->2->3 | f | {1,2,3} |
| 5 | 1 | 2 | 1->4->5 | f | {1,4,5} |
| 5 | 2 | 2 | 1->4->5 | f | {1,4,5} |
| 6 | 2 | 2 | 1->4->6 | f | {1,4,6} |
| 2 | 1 | 3 | 1->2->3->2 | t | {1,2,3,2} |
| 2 | 1 | 3 | 1->4->5->2 | f | {1,4,5,2} |
| 3 | 1 | 4 | 1->4->5->2->3 | f | {1,4,5,2,3} |
| 2 | 1 | 5 | 1->4->5->2->3->2 | t | {1,4,5,2,3,2} |
+-------+-------+--------+-------------------+-----------+---------------+
PostgreSQL 14将使用两个新子句
SEARCH
和CYCLE
扩展递归cte:
CYCLE子句首先指定要跟踪循环检测的列列表,然后指定显示是否已检测到循环的列名,然后指定在该列中用于是和否情况的两个值,最后指定将跟踪路径的另一列的名称。cycle和path列将隐式添加到CTE的输出行中
此处演示使用相同语法(Oracle):
带递归CTE的有向循环图的最短路径遍历。有趣的快速搜索可以找到很多关于这个主题的文章…谢谢你删除了重复的文章,这里有一篇有趣的文章+1,如果我有时间,我会玩这个。我已经有了答案(这是非常明显和简单的),但不会在这里提供:)想看看其他的选择。这大概是我的解决方案。除非你的循环仍然无法进入起始节点。但这很容易解决。不过,在接受之前,我会等待解决方案的其他选项。
+-------+-------+--------+-------------------+-----------+---------------+
| node | data | depth | path | is_cycle | path_array |
+-------+-------+--------+-------------------+-----------+---------------+
| 1 | 1 | 0 | 1 | f | {1} |
| 1 | 2 | 0 | 1 | f | {1} |
| 2 | 1 | 0 | 2 | f | {2} |
| 2 | 1 | 1 | 1->2 | f | {1,2} |
| 3 | 1 | 1 | 2->3 | f | {2,3} |
| 4 | 1 | 1 | 1->4 | f | {1,4} |
| 4 | 2 | 1 | 1->4 | f | {1,4} |
| 2 | 1 | 2 | 2->3->2 | t | {2,3,2} |
| 3 | 1 | 2 | 1->2->3 | f | {1,2,3} |
| 5 | 1 | 2 | 1->4->5 | f | {1,4,5} |
| 5 | 2 | 2 | 1->4->5 | f | {1,4,5} |
| 6 | 2 | 2 | 1->4->6 | f | {1,4,6} |
| 2 | 1 | 3 | 1->2->3->2 | t | {1,2,3,2} |
| 2 | 1 | 3 | 1->4->5->2 | f | {1,4,5,2} |
| 3 | 1 | 4 | 1->4->5->2->3 | f | {1,4,5,2,3} |
| 2 | 1 | 5 | 1->4->5->2->3->2 | t | {1,4,5,2,3,2} |
+-------+-------+--------+-------------------+-----------+---------------+
CYCLE id SET is_cycle TO true DEFAULT false USING path
WITH paths(node, data,depth,path) AS (
SELECT
src as node
, data as data
, 0 as depth
, TO_CHAR(src)as path
FROM edges
WHERE src IN (1,2)
UNION ALL
SELECT
edges.dst
, edges.data
, depth + 1
, paths.path || '->' || edges.dst
FROM paths
JOIN edges
ON edges.src = paths.node
AND edges.data = paths.data
) CYCLE node SET cycle TO 1 DEFAULT 0
SELECT DISTINCT * FROM paths;