簡體   English   中英

Oracle層次和(從葉到根的距離)

[英]Oracle hierarchical sum (distance from leaf to root)

我想獲得分層查詢(Oracle 11gR2)的幫助。 我很難處理這些問題......

事實上,這是一個2合1問題(需要2種不同的方法)。

我正在尋找一種方法來獲得從所有個體記錄到根的距離(而不是相反)。 我的數據是樹狀結構:

CREATE TABLE MY_TREE
(ID_NAME VARCHAR2(1) PRIMARY KEY,
 PARENT_ID VARCHAR2(1),
 PARENT_DISTANCE NUMBER(2)
);

INSERT INTO MY_TREE (ID_NAME,PARENT_ID,PARENT_DISTANCE) VALUES('A',NULL,NULL);
INSERT INTO MY_TREE (ID_NAME,PARENT_ID,PARENT_DISTANCE) VALUES('B','A',1);
INSERT INTO MY_TREE (ID_NAME,PARENT_ID,PARENT_DISTANCE) VALUES('C','B',3);
INSERT INTO MY_TREE (ID_NAME,PARENT_ID,PARENT_DISTANCE) VALUES('D','B',5);
INSERT INTO MY_TREE (ID_NAME,PARENT_ID,PARENT_DISTANCE) VALUES('E','C',7);
INSERT INTO MY_TREE (ID_NAME,PARENT_ID,PARENT_DISTANCE) VALUES('F','D',11);
INSERT INTO MY_TREE (ID_NAME,PARENT_ID,PARENT_DISTANCE) VALUES('G','D',13);

從層次上看,我的數據看起來像這樣(但我有多個獨立的根和更多級別):

在此輸入圖像描述

在第一個approch中,我正在尋找一個可以給我這個結果的查詢:

LEVEL ROOT NODE ID_NAME ROOT_DISTANCE
----- ---- ---- ------- -------------
1     A    null A       null
2     A    null B       1
3     A    B    C       4
4     A    B    E       11
3     A    B    D       6
4     A    D    F       17
4     A    D    G       19

在這個結果中,

  • “NODE”列表示最近的拆分元素的ID_NAME
  • “ROOT_DISTANCE”列表示從元素到根的距離(例如:ID_NAME的ROOT_DISTANCE = G是從G到A的距離:G(13)+ D(5)+ B(1)= 19)

在這個approch中,我將始終指定最多2個根。

第二個approch必須是一個PL / SQL腳本,它將執行相同的計算(ROOT_DISTANCE),但是以迭代的方式,並將結果寫入新表。 我想一次運行這個腳本,所以將處理所有的根(~1000)。

這是我看到腳本的方式:

  • 對於所有根,我們需要找到關聯的葉子,然后計算從葉子到根的距離(對於葉子和根之間的所有元素)並將其放入表中。

“性能視角”需要此腳本,因此如果已經計算了一個元素(例如:由另一個葉子計算的分割節點),我們需要停止計算並傳遞給下一個葉子,因為我們已經知道了結果從那里到根。 例如,如果系統計算ECBA,然后計算FDBA,則不應再次計算BA部分,因為它是在第一次傳遞中完成的。

你可以解決其中一個或兩個問題,但我需要這兩個問題的靈感。

謝謝!

試試這個:

WITH brumba(le_vel,root,node,id_name,root_distance) AS (
  SELECT 1 as le_vel, id_name as root, null as node, id_name, to_number(null) as root_distance  
  FROM MY_TREE WHERE parent_id IS NULL
  UNION ALL
  SELECT b.le_vel + 1, b.root, 
         CASE WHEN 1 < (
                SELECT count(*) FROM MY_TREE t1 WHERE t1.parent_id = t.parent_id
              )
              THEN t.parent_id ELSE b.node
         END,
         t.id_name, coalesce(b.root_distance,0)+t.parent_distance
  FROM MY_TREE t
  JOIN brumba b ON b.id_name = t.parent_id
)
SELECT * FROM brumba

演示: https//dbfiddle.uk/?rbms = aracle_11.2&tele = d5c231055e989c3cbcd763f4b3d3033f


使用PL / SQL不需要“第二個approch” - 上面的SQL將一次計算所有根節點(在parent_id列中為null)的結果。
只需在上面的查詢中添加INSERT INTO tablename(col1,col2, ... colN) ...CREATE TABLE name AS ...的前綴。
上面的演示包含后一個選項CREATE TABLE xxx AS query的示例

這是一個選項,顯示如何獲得問題的第一部分:

SQL> with temp as
  2    (select level lvl,
  3            ltrim(sys_connect_by_path(id_name, ','), ',') path
  4     from my_tree
  5     start with parent_id is null
  6     connect by prior id_name = parent_id
  7    ),
  8  inter as
  9    (select t.lvl,
 10            t.path,
 11            regexp_substr(t.path, '[^,]+', 1, column_value) col
 12     from temp t,
 13          table(cast(multiset(select level from dual
 14                              connect by level <= regexp_count(path, ',') + 1
 15                             ) as sys.odcinumberlist ))
 16    )
 17  select i.lvl,
 18         i.path,
 19         sum(m.parent_distance) dis
 20  from inter i join my_tree m on m.id_name = i.col
 21  group by i.lvl, i.path
 22  order by i.path;

 LVL PATH              DIS
---- ---------- ----------
   1 A
   2 A,B                 1
   3 A,B,C               4
   4 A,B,C,E            11
   3 A,B,D               6
   4 A,B,D,F            17
   4 A,B,D,G            19

7 rows selected.

SQL>

以下是使用分層( connect by )查詢解決此問題的方法。

在大多數分層問題中,分層查詢將比遞歸查詢更快( with子句的遞歸)。 然而,你的問題不是純粹的層次-你需要計算到根的距離,而不像遞歸with ,你不能在單次使用層次查詢。 所以聽到你的消息會很有趣! - 這些方法之間是否存在任何顯着的性能差異。 對於它的價值,在您提供的非常小的數據樣本上,優化器使用connect by估計成本為5,而遞歸解決方案則為48; 這是否意味着在現實生活中你會發現什么,希望你也會告訴我們。

在分層查詢中,我標記了有兩個或更多子節點的父節點(我為此使用了分析函數,以避免連接)。 然后我構建了層次結構,並在最后一步中聚合以獲得所需的位。 在遞歸解決方案中,這應該為您提供所需的一切 - 對於所有根和所有節點 - 在單個SQL查詢中; 不需要PL / SQL代碼。

with
  branching (id_name, parent_id, parent_distance, b) as (
    select id_name, parent_id, parent_distance, 
           case when count(*) over (partition by parent_id) > 1 then 'y' end
    from   my_tree
  )
, hierarchy (lvl, leaf, id_name, parent_id, parent_distance, b) as (
    select  level, connect_by_root id_name, id_name, parent_id, parent_distance, b
    from    branching
    connect by id_name = prior parent_id
  )
select   max(lvl) as lvl, 
         min(id_name) keep (dense_rank last order by lvl) as root,
         leaf as id_name,
         min(decode(b, 'y', parent_id)) 
           keep (dense_rank first order by decode(b, 'y', lvl)) as node,
         sum(parent_distance) as root_distance
from     hierarchy
group by leaf;

LVL ROOT    ID_NAME NODE    ROOT_DISTANCE
--- ------- ------- ------- -------------
  1 A       A                            
  2 A       B                           1
  3 A       C       B                   4
  3 A       D       B                   6
  4 A       E       B                  11
  4 A       F       D                  17
  4 A       G       D                  19

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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