2016-08-30 53 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.