在分层查询中忽略单个孩子(竹子部分)
Ignoring single childs (bamboo parts) in hierarchical query
我有一个 table 具有如下分层数据。
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
语句遍历层次结构很简单。
我的提取要求是忽略树的简单(竹子状)部分,即如果 parent 只有一个 child,则两者都连接在一起并且 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
到目前为止,我设法计算了 child 并构建了 child 计数和 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 上跳过一级(一个尾随 child 计数 1)和在 ID 6 上跳过 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);
这不是很优雅,但应该可以。如果我能找到更好的方法来完成最后一部分,我会进行编辑。祝你好运!
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
;
我有一个 table 具有如下分层数据。
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
语句遍历层次结构很简单。
我的提取要求是忽略树的简单(竹子状)部分,即如果 parent 只有一个 child,则两者都连接在一起并且 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
到目前为止,我设法计算了 child 并构建了 child 计数和 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 上跳过一级(一个尾随 child 计数 1)和在 ID 6 上跳过 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);
这不是很优雅,但应该可以。如果我能找到更好的方法来完成最后一部分,我会进行编辑。祝你好运!
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
;