我在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
我的目标是获得节点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;
这样一个查询的问题是它会遍历所有可能的路径,直到它们循环,并且在我的实际数据中有太多它们。结果包含许多重复项 - 这是:
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
我的实际数据要复杂得多。执行此类查询的成本非常高,以至于我的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
我遇到的问题是:
ORA-32033: unsupported column aliasing
,有些客户使用Oracle 9或10),ORA-32041: UNION ALL operation in recursive WITH clause must have only two branches
。如果我删除MINUS子句,我将获得周期(ORA-32044: cycle detected while executing recursive WITH query
)。 您如何查询原始数据以有效获取节点3,4,5,6,8? PL / SQL解决方案也很受欢迎。
谢谢。
答案 0 :(得分:2)
到达任何子节点的预期最大深度是多少?
如果它相对较小,你可以循环下来,同时检查你已经访问过的节点,就像这样......
(注意,我不是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;
根据效果,您可能还需要depth
中的myMap
字段;优化连接以便仅加入最近的节点。这意味着两个指标;一个用于JOIN (depth)
,另一个用于NOT EXISTS (parent)
。
修改强>
添加了DISTINCT关键字,以避免以下情况......
- 节点2映射到3和4
- 节点3和4都映射到节点5
- 节点5的所有子节点现在将被处理两次
GROUP BY或许多其他选项可用于满足此要求,而不是DISTINCT。只是它本身的NOT EXISTS是不够的。
答案 1 :(得分:1)
我自己没有使用过这个,但是使用NOCYCLE选项的CONNECT BY怎么样?这应该会在看到循环时停止遍历树。 Oracle 11i肯定有这个,我认为它出现在Oracle 10g时期的某个地方。
答案 2 :(得分:1)
这可能有助于访问超过4000字节。循环不应该是可能的,但这条线就是一个例子。
WITH descendants(node, lvl, pth, visited) AS
(
SELECT child node, 1, cast(child as varchar2(4000)), '/'||listagg(child,'/') within group (order by child) over()||'/'
FROM t
where parent = 2
UNION ALL
SELECT child, lvl+1, pth||'/'||child, D.visited||listagg(child,'/') within group (order by child) over()||'/'
FROM T
INNER JOIN descendants D
ON T.parent = D.node
WHERE D.visited not like '%/'||child||'/%'
)
cycle node set cyc to '1' default '0'
SELECT distinct node
FROM descendants
order by node
;