Ben*_*oit 14 sql oracle plsql data-structures
我在Oracle表中提供数据,该表被组织为可以包含循环的图(参见示例).
CREATE TABLE T (parent INTEGER, child INTEGER)
AS select 1 parent, 2 child from dual
union all select 1 parent, 8 child from dual
union all select 2 parent, 3 child from dual
union all select 2 parent, 4 child from dual
union all select 2 parent, 8 child from dual
union all select 3 parent, 4 child from dual
union all select 3 parent, 6 child from dual
union all select 4 parent, 5 child from dual
union all select 5 parent, 8 child from dual
union all select 6 parent, 5 child from dual
union all select 7 parent, 3 child from dual
union all select 7 parent, 5 child from dual
union all select 8 parent, 6 child from dual
Run Code Online (Sandbox Code Playgroud)

我的目标是获得节点X的后代(孩子,孩子的孩子等)的所有节点.假设2.我的预期结果是:3,4,5,6,8.
我知道我可以设计一个这样的查询:
SELECT child, sys_connect_by_path(child,'/')
FROM T
START WITH parent = 2
CONNECT BY NOCYCLE PRIOR child = PARENT;
Run Code Online (Sandbox Code Playgroud)
这种查询的问题在于它将遍历所有可能的路径,直到它们循环,并且在我的实际数据中有太多它们.结果包含许多重复 - 这是:
child | sys_connect_by_path (for information)
3 | /3
4 | /3/4
5 | /3/4/5
8 | /3/4/5/8
6 | /3/4/5/8/6
6 | /3/6
5 | /3/6/5
8 | /3/6/5/8
4 | /4
5 | /4/5
8 | /4/5/8
6 | /4/5/8/6
8 | /8
6 | /8/6
5 | /8/6/5
Run Code Online (Sandbox Code Playgroud)
我的实际数据要复杂得多.执行此类查询的成本非常高,以至于我的TEMP表空间(可自动扩展)达到了10Gb(最初为500 Mb),而且由于磁盘已满,我的数据库实际上已损坏.
我尝试像这样设计查询(递归WITH子句):
WITH descendants(node) AS
( SELECT 2 node FROM dual
UNION ALL
(
SELECT child
FROM T
INNER JOIN descendants D
ON T.parent = D.node
MINUS SELECT node FROM descendants
)
)
SELECT * FROM descendants
Run Code Online (Sandbox Code Playgroud)
我遇到的问题是:
ORA-32033: unsupported column aliasing有些客户使用Oracle 9或10),ORA-32041: UNION ALL operation in recursive WITH clause must have only two branches.如果我删除MINUS子句,我将得到cycles(ORA-32044: cycle detected while executing recursive WITH query).您如何查询我的原始数据以有效地获取这些节点3,4,5,6,8?PL/SQL解决方案也是受欢迎的.
谢谢.
您期望到达任何子节点的最大深度是多少?
如果它相对较小,您可以循环下来,同时检查您已经访问过的节点,以类似这样的方式......
(注意,我不是 Oracle 专家,所以这更接近于混合了一点真实 SQL 的伪代码)
CREATE TABLE myMap (parent INT, child INT);
INSERT INTO myTable SELECT NULL, 2 FROM DUAL;
WHILE (SQL%ROWCOUNT > 0)
LOOP
INSERT INTO
myMap
SELECT DISTINCT
dataMap.parent,
dataMap.child
FROM
myMap
INNER JOIN
dataMap
ON myMap.child = dataMap.parent
WHERE
NOT EXISTS (SELECT * FROM myMap WHERE parent = dataMap.parent)
END LOOP;
Run Code Online (Sandbox Code Playgroud)
根据性能,您可能还需要一个depth字段myMap;优化连接,以便仅连接最近的节点。这意味着两个索引;一种用于 JOIN (depth),一种用于 NOT EXISTS (parent)。
编辑
添加了 DISTINCT 关键字,以避免出现以下情况...
- 节点 2 映射到 3 和 4
- 节点 3 和 4 都映射到节点 5
- 节点 5 的所有子节点现在将被处理两次
GROUP BY 或许多其他选项可以用来代替 DISTINCT 来满足此要求。只是 NOT EXISTS 本身是不够的。