如何在oracle中获取子节点的树层次结构?

时间:2016-08-30 18:52:02

标签: sql oracle oracle11g hierarchical-data recursive-query

我需要在分层查询中写入帮助以获取子节点的父路径。我正在尝试使用函数sys_connect_by_path,但我无法这样做,因为具有父标题的函数的结果超出了column(4000 chars)的最大字符限制。因此,我需要将自定义集合中的路径保存到clob中,我发现很难找到它。

示例:

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;

我需要一个可以容纳超过4000个字符的clob / custom集合。

2 个答案:

答案 0 :(得分:1)

只要您使用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,这使它保持为该数据类型。

您可以剪掉尾随的逗号/栏,或稍微修改一下查询,以免它们出现:

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

答案 1 :(得分:0)

SYS_CONNECT_BY_PATH几乎是LISTAGG的应用程序,如下所示:首先生成所需的行,包括CONNECT_BY_ROOTLEVEL,然后汇总。如下所示,更明确地执行此操作,可以让您更准确地控制聚合中的内容,使用级别的顺序等等。(注意:我不认为这就是Oracle在内部执行的方式,因为LISTAGG的添加时间比SYS_CONNECT_BY_PATH晚得多,但逻辑上就是它的工作方式。)

所以问题无论是4,000个字符的限制。与LISTAGG函数并排,我使用XMLAGG显示不同的聚合 - 它没有4,000个字符限制。对于大输入数据,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.