[英]Oracle hierarchical query on non-hierarchical data
我在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解決方案。
謝謝。
您到達任何子節點的最大預期深度是多少?
如果它比較小,您可以像下面這樣循環播放,同時檢查已訪問的節點...
(請注意,我不是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;
根據性能,您可能還需要myMap
的depth
字段; 優化聯接,以便僅在最新節點上聯接。 這意味着兩個索引; 一個用於JOIN (depth)
,另一個用於NOT EXISTS (parent)
。
編輯
添加了DISTINCT關鍵字,以避免出現以下情況...
-節點2映射到3和4
-節點3和4都映射到節點5
-現在將對節點5的所有子節點進行兩次處理
GROUP BY或許多其他選項可以代替DISTINCT來滿足此要求。 僅僅依靠NOT EXISTS本身是不夠的。
我本人尚未為此工作,但是使用NOCYCLE選項的CONNECT BY呢? 當它看到循環時,應該停止遍歷樹。 Oracle 11i確實具有這一點,我認為它是在Oracle 10g時代出現的。
這可能會有所幫助,直到訪問量超過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
;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.