2016-07-24 40 views
3

我有一个分层数据如下的表格。忽略分层查询中的单个孩子(竹子部分)

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级(计数路径中的两个训练数据)。

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

回答

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 
; 
+0

好想法连接节点与一级。我会在重新考虑它之后明天接受:) –

+0

我无法想出一种方法来避免最后的两个连接。如果我可以跟踪竹节中第一个节点的父节点而不需要加入(在原始表中查找),那将会很好。但在这方面我想到的任何东西都会使首先鉴定竹制部件变得更加繁琐。祝你好运! – mathguy

1

此查询将帮助您找到其他解决方案。

尽管可能会进一步优化或修复一些错误,但它适用于您的测试用例。

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);