【发布时间】:2018-05-23 09:08:16
【问题描述】:
我有一个简单的有向图结构,形式为:
CREATE TABLE node (id NUMBER(8, 0), NAME VARCHAR2(100));
CREATE TABLE edge (parent NUMBER(8, 0), child NUMBER(8, 0));
其中node 存储所有节点,edge 存储从parent 到child 的所有有向边。
我现在想要的是一个视图,它显示从所有根(没有传入边的节点)到图中所有其他节点(包括零长度路径)的所有路径。该图保证没有循环。
我使用了使用子查询分解的分层查询,结果如下:
WITH rec_view(id, parent, lvl, root_id, path) AS (
SELECT
id
, NULL
, 1 lvl
, id root_id
, '/' || TO_CHAR(id) path
FROM node g
WHERE NOT EXISTS (SELECT 1 FROM edge h WHERE h.child = g.id)
UNION ALL
SELECT
hier.child
, prev.id
, prev.lvl + 1 lvl
, prev.root_id
, prev.path || '/' || hier.child path
FROM rec_view prev
JOIN edge hier ON prev.id = hier.parent
)
SELECT
n.id, n.NAME, v.root_id, v.lvl, v.path
FROM
rec_view v
JOIN node n ON v.id = n.id
有了这个测试数据,我得到了 10 行的预期结果。
INSERT INTO node VALUES (1, 'KBR');
INSERT INTO node VALUES (2, 'H');
INSERT INTO node VALUES (3, 'N');
INSERT INTO node VALUES (4, 'KBR H');
INSERT INTO node VALUES (5, 'KBR N');
INSERT INTO node VALUES (6, 'Dummy');
INSERT INTO node VALUES (7, 'Nach H');
INSERT INTO edge VALUES (1, 4);
INSERT INTO edge VALUES (1, 5);
INSERT INTO edge VALUES (2, 4);
INSERT INTO edge VALUES (3, 5);
INSERT INTO edge VALUES (4, 7);
ID Name Root Level Path
------------------------------
1 KBR 1 1 /1
2 H 2 1 /2
3 N 3 1 /3
4 KBR H 1 2 /1/4
4 KBR H 2 2 /2/4
5 KBR N 1 2 /1/5
5 KBR N 3 2 /3/5
6 Dummy 6 1 /6
7 Nach H 2 3 /2/4/7
7 Nach H 1 3 /1/4/7
但是,我想要的是一个使用 Oracles CONNECT BY 功能的查询,因为我认为它比子查询分解更容易阅读和理解,它也是我们公司分层查询的首选方式。但是我提出的问题给我带来了太多的结果。我得到的是这样的:
SELECT
parent.id id
, parent.NAME NAME
, connect_by_root parent.NAME root_id
, LEVEL lvl
, sys_connect_by_path(parent.id, '/') path
FROM
node parent
LEFT JOIN edge hier ON parent.id = hier.parent
START WITH
NOT EXISTS (SELECT 1 FROM edge h WHERE h.child = parent.id)
CONNECT BY
PRIOR hier.child = parent.id
这里我得到 11 行,而不是路径 /1 的行包含两次的 10 行。
ID Name Root Level Path
-------------------------------
1 KBR 1 1 /1
4 KBR H 1 2 /1/4
7 Nach H 1 3 /1/4/7
1 KBR 1 1 /1
5 KBR N 1 2 /1/5
2 H 2 1 /2
4 KBR H 2 2 /2/4
7 Nach H 2 3 /2/4/7
3 N 3 1 /3
5 KBR N 3 2 /3/5
6 Dummy 6 1 /6
我的查询有什么问题,如何解决这个问题以仅包含所需的行?
【问题讨论】: