2016-08-30 56 views
2

我需要幫助在分層查詢中獲取子節點的父路徑。我試圖使用函數sys_connect_by_path,但我無法這樣做,因爲具有父標題的函數的結果超出了column(4000 chars)的最大字符數限制。所以我需要在自定義集合中保留路徑,或者需要在clob中找到我很難想出的路徑。如何在oracle中爲子節點獲取樹的層次結構?

例如:

contentid  -   parentid 
0   -    null 
1    -    0 
2    -    1 
3   -    2 
4   -    2 
5   -    6 
6   -    3 
7   -    6 

預期結果:

contentid  -  Expected result set 
0   -    null 
1    -    0 
2   -    1,0 
3   -    2,1,0 
4   -    2,1,0 
5   -    6,3,2,1,0 
6   -    3,2,1,0 
7   -    6,3,2,1,0 

查詢來獲取父路徑的子節點插入一列

SELECT CHILD_ID, 
     PATH 
FROM (SELECT sys_connect_by_path(CHILD_TITLE, '|') PATH 
      , connect_by_root(PARENT_ID) ROOT_ID, CHILD_ID 
     FROM table 
    CONNECT BY PRIOR CHILD_ID = PARENT_ID 
     ORDER BY CHILD_ID) 
WHERE ROOT_ID IS NULL; 

我需要它在CLOB /自定義可容納超過4000個字符的集合。

+0

只是爲了澄清。你的問題不是預期的結果,而是4k的限制? –

+0

是的,我的問題是4k的限制。我能夠使用CONNECT BY PRIOR獲取路徑。但結果預計會超過4k的限制。 – ujwal

+0

我有一個備份方法來創建一個臨時表結果集到一個clob,我不願意做,因爲主表預計會頻繁更改,我需要關心所有的子節點,如果中間節點是除去。 – ujwal

回答

0

只要你在11gR2或更高版本上,你可以使用recursive subquery factoring而不是connect by分層語法。

如果你的表稱爲t有:

CHILD_ID PARENT_ID CHILD_T 
---------- ---------- ------- 
     0   root 
     1   0 first 
     2   1 second 
     3   2 third 
     4   2 fourth 
     5   6 fifth 
     6   3 sixth 
     7   6 seventh 

你可以這樣做:

with r (child_id, child_title, id_path, title_path) as (
    select child_id, child_title, to_clob(null), to_clob(null) 
    from t 
    where parent_id is null 
    union all 
    select t.child_id, t.child_title, 
    t.parent_id ||','|| r.id_path, r.child_title ||'|'|| r.title_path 
    from r 
    join t on t.parent_id = r.child_id 
) 
select child_id, id_path, title_path 
from r 
order by child_id; 

    CHILD_ID ID_PATH    TITLE_PATH 
---------- -------------------- ---------------------------------------- 
     0 
     1 0,     root| 
     2 1,0,     first|root| 
     3 2,1,0,    second|first|root| 
     4 2,1,0,    second|first|root| 
     5 6,3,2,1,0,   sixth|third|second|first|root| 
     6 3,2,1,0,    third|second|first|root| 
     7 6,3,2,1,0,   sixth|third|second|first|root| 

錨構件轉動路徑轉換的CLOB;遞歸成員將每個標題附加到CLOB,CLOB將其保留爲該數據類型。

可以剪掉後面的逗號/條,或修改查詢了一點,所以他們從來沒有出現:

with r (parent_id, child_id, child_title, id_path, title_path) as (
    select parent_id, child_id, child_title, to_clob(null), to_clob(null) 
    from t 
    where parent_id is null 
    union all 
    select t.parent_id, t.child_id, t.child_title, 
    t.parent_id || case when r.parent_id is not null then ',' end || r.id_path, 
    r.child_title || case when r.parent_id is not null then '|' end || r.title_path 
    from r 
    join t on t.parent_id = r.child_id 
) 
select child_id, id_path, title_path 
from r 
order by child_id; 

    CHILD_ID ID_PATH    TITLE_PATH 
---------- -------------------- ---------------------------------------- 
     0 
     1 0     root 
     2 1,0     first|root 
     3 2,1,0    second|first|root 
     4 2,1,0    second|first|root 
     5 6,3,2,1,0   sixth|third|second|first|root 
     6 3,2,1,0    third|second|first|root 
     7 6,3,2,1,0   sixth|third|second|first|root 

你的樣本值沒有表現出一個CLOB的需要,但在更多的數據相加到虛擬表顯示生成的值可超過4K:

insert into t 
select level + 7, level + 6, 'title' 
from dual 
connect by level <= 2000; 

with r (...) -- as above 
select max(length(id_path)), max(length(title_path)) 
from r; 

MAX(LENGTH(ID_PATH)) MAX(LENGTH(TITLE_PATH)) 
-------------------- ----------------------- 
       8920     12031 
0

SYS_CONNECT_BY_PATH是相當多的LISTAGG的應用程序,如下所示:首先你生成所需的行,包括CONNECT_BY_ROOTLEVEL,然後你聚合。正如我在下面顯示的那樣,更明確地做一點,可以讓您更精確地控制聚合中的內容,以何種順序使用關卡等等。(注意:我不認爲這就是Oracle它在內部,因爲LISTAGG被添加比SYS_CONNECT_BY_PATH晚得多,但在邏輯上這是它的工作原理。)

所以這個問題的任何一種方法是4,000字符的限制。與LISTAGG函數並排顯示不同的聚合,使用XMLAGG - 它不具有4000個字符的限制。對於大輸入數據,LISTAGG行不起作用,但XMLAGG行將正常工作,並將生成CLOB。祝你好運!

查詢

with 
    t (child_id, parent_id) as (
     select 0, null from dual union all 
     select 1, 0 from dual union all 
     select 2, 1 from dual union all 
     select 3, 2 from dual union all 
     select 4, 2 from dual union all 
     select 5, 6 from dual union all 
     select 6, 3 from dual union all 
     select 7, 6 from dual 
    ) 
select child_id, 
     listagg(parent_id, ',') within group (order by lvl) as gen_tree_1, 
     rtrim(xmlcast(xmlagg(xmlelement(e, parent_id||',') order by lvl) as clob), ',') 
                  as gen_tree_2 
from (select connect_by_root child_id as child_id, parent_id, level as lvl 
      from t 
      connect by child_id = prior parent_id 
     ) 
group by child_id 
order by child_id 
; 

輸出

CHILD_ID GEN_TREE_1   GEN_TREE_2 
---------- -------------------- -------------------- 
     0 
     1 0     0 
     2 1,0     1,0 
     3 2,1,0    2,1,0 
     4 2,1,0    2,1,0 
     5 6,3,2,1,0   6,3,2,1,0 
     6 3,2,1,0    3,2,1,0 
     7 6,3,2,1,0   6,3,2,1,0 

8 rows selected.