【问题标题】:Ignoring single childs (bamboo parts) in hierarchical query在分层查询中忽略单个子项(竹子部分)
【发布时间】:2016-11-27 19:35:49
【问题描述】:

我有一个包含分层数据的表,如下所示。

create table tst as 
select 1 id, null parent_id from dual union all
select 2 id, 1 parent_id from dual union all
select 3 id, 1 parent_id from dual union all
select 4 id, 2 parent_id from dual union all
select 5 id, 3 parent_id from dual union all
select 6 id, 5 parent_id from dual union all
select 7 id, 6 parent_id from dual union all
select 8 id, 6 parent_id from dual;

使用CONNECT BY 语句遍历层次结构很简单。

我的提取要求是忽略树的简单(竹状)部分,即如果父母只有一个孩子,则两者都被连接并且 ID 被连接(递归应用此规则)。

所以预期的结果是

       ID  PARENT_ID
---------- ----------
         1            
         2,4        1 
         3,5,6      1 
         7          3,5,6 
         8          3,5,6 

UPDATE 或者这也是正确的答案(添加连接的节点列表并重用原始 IDS)

        ID  PARENT_ID NODE_LST 
---------- ---------- ---------
         1            1       
         4          1 2,4     
         6          1 3,5,6   
         7          6 7       
         8          6 8

到目前为止,我设法计算了孩子的数量并构建了孩子数量和 ID 的根的完整路径...

with child_cnt as (
-- child count per parent
select parent_id, count(*) cnt 
from tst
where parent_id is not NULL
group by parent_id),
tst2 as (
select 
  ID,  child_cnt.cnt,
  tst.parent_id
from tst left outer join child_cnt on tst.parent_id = child_cnt.parent_id),
tst3 as (
SELECT id, parent_id,
  sys_connect_by_path(cnt,',') child_cnt_path,
  sys_connect_by_path(id,',') path
FROM tst2
  START WITH parent_id IS NULL
  CONNECT BY  parent_id  = PRIOR id
)
select * from tst3
;


        ID  PARENT_ID CHILD_CNT_PATH PATH       
---------- ---------- -------------- ------------
         1            ,              ,1           
         2          1 ,,2            ,1,2         
         4          2 ,,2,1          ,1,2,4       
         3          1 ,,2            ,1,3         
         5          3 ,,2,1          ,1,3,5       
         6          5 ,,2,1,1        ,1,3,5,6     
         7          6 ,,2,1,1,2      ,1,3,5,6,7   
         8          6 ,,2,1,1,2      ,1,3,5,6,8   

这表明在 ID 4 和 5 上要跳过一个级别(一个尾随的孩子计数 1),在 ID 6 上跳过 2 级(计数路径中的两个训练)。

但我认为应该有一个更简单的方法来解决这个问题。

【问题讨论】:

    标签: oracle hierarchical-query


    【解决方案1】:

    这不是很优雅,但应该可以。如果我能找到更好的方法来完成最后一部分,我会进行编辑。祝你好运!

    with
         d ( id, parent_id, degree ) as (
           select id, parent_id, count(parent_id) over (partition by parent_id)
           from   tst
         ),
         x ( old_id, new_id ) as (
           select id, ltrim(sys_connect_by_path(id, ','), ',')
           from   d
           where connect_by_isleaf = 1
           start with degree != 1
           connect by parent_id = prior id
           and        degree = 1
         )
    select x1.new_id as id, x2.new_id as parent_id
    from   x x1 
                inner join tst 
                     on tst.id        = regexp_substr(x1.new_id, '^[^,]+')
                left outer join x x2
                     on tst.parent_id = x2.old_id
    ;
    

    【讨论】:

    • 用一级连接节点的好主意。明天重新考虑后我会接受:)
    • 我想不出一种方法来避免最后的两个连接。如果我可以在不需要连接的情况下跟踪竹子部分中第一个节点的父节点(在原始表中查找),那就太好了;但是我在这方面想到的一切都会使首先识别竹子部分变得更加麻烦。祝你好运!
    【解决方案2】:

    此查询将带您找到替代解决方案。

    虽然可能需要修复一些进一步的优化或错误,但它适用于您的测试用例。

    WITH nodes_to_dispose as (
        SELECT min(id) as id,
               parent_id
        FROM tst
        WHERE parent_id is not null
        GROUP BY parent_id
        HAVING count(*) = 1 )
    -- This part returns merged bamboo nodes
    SELECT nodes_to_dispose.id,
           connect_by_root tst.parent_id as parent_id,
           connect_by_root nodes_to_dispose.parent_id ||
                   sys_connect_by_path(nodes_to_dispose.id, ',') as node_lst
    FROM nodes_to_dispose, tst
    WHERE nodes_to_dispose.parent_id = tst.id (+)
    AND connect_by_isleaf = 1
    START WITH nodes_to_dispose.parent_id not in (
        SELECT id
        FROM nodes_to_dispose )
    CONNECT BY prior nodes_to_dispose.id = nodes_to_dispose.parent_id
    UNION
    -- This part returns all other nodes in their original form
    SELECT id, parent_id, to_char(id) as node_lst
    FROM tst
    WHERE id not in (
        SELECT parent_id
        FROM nodes_to_dispose
        UNION
        SELECT id
        FROM nodes_to_dispose);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-05-12
      • 2016-04-19
      • 1970-01-01
      • 1970-01-01
      • 2022-12-11
      • 2014-06-14
      • 1970-01-01
      相关资源
      最近更新 更多