考虑以下有向图:
我希望能够在此图中选择两个节点,然后找到这两个节点之间的所有可能路径。 (好吧,不是所有路径,只是那些不包含循环的路径。)
首先,我在视图中模拟图形(作为传递闭包)
CREATE OR REPLACE VIEW closure AS
WITH
edges AS (
-- for demonstration purposes, I just need to model the edges
SELECT 'A' AS start_node, 'C' AS end_node FROM dual UNION ALL
SELECT 'A', 'D' FROM dual UNION ALL
SELECT 'B', 'C' FROM dual UNION ALL
SELECT 'B', 'D' FROM dual UNION ALL
SELECT 'C', 'E' FROM dual UNION ALL
SELECT 'D', 'F' FROM dual UNION ALL
SELECT 'E', 'A' FROM dual UNION ALL
SELECT 'E', 'F' FROM dual UNION ALL
SELECT 'E', 'G' FROM dual UNION ALL
SELECT 'F', 'B' FROM dual UNION ALL
SELECT 'F', 'G' FROM dual
)
SELECT
CONNECT_BY_ROOT start_node AS start_node,
end_node AS end_node,
CONNECT_BY_ROOT start_node||sys_connect_by_path(end_node, ' -> ') AS path,
level + 1 AS path_length
FROM
edges
CONNECT BY
NOCYCLE PRIOR end_node = start_node
-- this bit just adds the trivial paths to the closure
UNION ALL
SELECT DISTINCT start_node, start_node, start_node, 1 FROM edges
;
view CLOSURE created.
现在,假设我想看到所有从'E'开始并以'G'结束的路径。
SELECT path, path_length
FROM closure
WHERE start_node='E'
AND end_node = 'G'
ORDER BY path_length ASC
;
PATH PATH_LENGTH
------------------------------ -----------
E -> G 2
E -> F -> G 3
E -> A -> C -> E -> G 5
E -> A -> D -> F -> G 5
E -> F -> B -> C -> E -> G 6
E -> A -> C -> E -> F -> G 6
E -> A -> D -> F -> B -> C -> E -> G 8
7 rows selected
我很惊讶第三,第五,第六和第七行被退回。这四行都包含一个循环(具体来说,从'E'到'E')。
查看我视图中的数据,似乎包含的唯一周期是包含根节点的周期。 (例如,循环'E' - >'A' - >'C' - >'E')仅包含在以节点'E'开头的路径中...它永远不会包含在开始的路径中与任何其他节点。)
我试图通过在我的“CONNECT BY”子句中添加CONNECT_BY_ROOT start_node <> start_node
来防止这些周期发生,但Oracle不允许这样做。
如何防止在我的数据中出现这些周期?
====开始编辑====
正如@Egor Skriptunoff在下面指出的那样,我正在处理一个边缘表,所以也许我应该期望看到一些顶点重复,只要它们不在同一边缘上访问。但是,如果是这种情况,我应该看到路径'E - &gt; F - &gt; B - &gt; D - &gt; F - &gt; G'在上面的结果中。这是一个循环通过节点'F'的路径,一次通过边'E - &gt; F'和一次通过'D - &gt; F'。但是,这条路并不存在。
我认为这是因为NOCYCLE阻止节点'F'发生两次。这是真的吗?
Oracle版:Oracle Database 11g企业版11.2.0.3.0版 - 64位生产
====结束编辑====
答案 0 :(得分:1)
你的select子句有缺陷 - 你必须将CONNECT_BY_ROOT应用到边缘的头部而不是它的尾部
-- ...
CONNECT_BY_ROOT end_node AS start_node,
end_node AS end_node,
sys_connect_by_path(end_node, ' -> ') AS path,
level AS path_length
-- ...
答案 1 :(得分:1)
您正在使用边缘表,因此重复的边缘被排除,顶点可能会重复。