簡體   English   中英

Oracle對非分層數據的分層查詢

[英]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

我遇到的問題是:

  • 在Oracle 10g中,此功能未實現( ORA-32033: unsupported column aliasing ,有些客戶使用Oracle 9或10),
  • 使用Oracle 11g,我得到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;

根據性能,您可能還需要myMapdepth字段; 優化聯接,以便僅在最新節點上聯接。 這意味着兩個索引; 一個用於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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM