如何解决Oracle SQL:如何获取树中根节点和任何节点之间的第 n 个节点优化中
我试图从给定节点开始从根中找到第 N 个节点。如果给定节点小于或等于从根开始的第 N 个节点,则返回给定节点。我的查询有效,但速度很慢,我对此并不满意。关于如何重构它以提高性能的任何想法?
test_data
---------
unique_id | lookup_acct_code
'1' 'leaf-1'
'2' 'stem-2'
'3' 'branch-1'
'4' 'trunk-2'
'5' 'root-1'
linked_accounts
---------------
acct_code | parent_code
'leaf-1' 'stem-1'
'stem-1' 'twig-1'
'twig-1' 'stick-1'
'stick-1' 'branch-1'
'branch-1' 'trunk-1'
'trunk-1' 'root-1'
'root-1' NULL
'leaf-2' 'stem-2'
'stem-2' 'twig-2'
'twig-2' 'stick-2'
'stick-2' 'branch-2'
'branch-2' 'trunk-2'
'trunk-2' 'root-2'
'root-2' NULL
SELECT unique_id,(SELECT acct_code
FROM (SELECT acct_code,ROW_NUMBER() OVER (ORDER BY level desc) rn
FROM linked_accounts
CONNECT BY acct_code = PRIOR parent_code
START WITH acct_code = lookup_acct_code)
WHERE rn <= 3
ORDER BY rn DESC
FETCH FIRST 1 ROWS ONLY)
FROM test_data;
/* Correct output
1 branch-1
2 branch-2
3 branch-1
4 trunk-2
5 root-1
*/
解决方法
您可以首先从根生成值直到第 nth 个节点(使用递归子查询分解子句而不是分层查询),然后加入而不是使用相关子查询:
WITH from_roots ( acct_code,depth,nth_from_root ) AS (
SELECT acct_code,1,acct_code
FROM linked_accounts
WHERE parent_code IS NULL
UNION ALL
SELECT l.acct_code,depth + 1,CASE
WHEN depth >= 3
THEN nth_from_root
ELSE l.acct_code
END
FROM linked_accounts l
INNER JOIN from_roots f
ON ( f.acct_code = l.parent_code )
)
SELECT unique_id,nth_from_root
FROM from_roots f
INNER JOIN test_data d
ON ( d.lookup_acct_code = f.acct_code )
或者,您可以调整查询以使用 MAX(LEVEL) OVER () - LEVEL + 1
直接计算深度:
SELECT unique_id,( SELECT acct_code
FROM (
SELECT acct_code,MAX(LEVEL) OVER () - LEVEL + 1 AS depth
FROM linked_accounts
CONNECT BY acct_code = PRIOR parent_code
START WITH acct_code = lookup_acct_code
)
WHERE depth <= 3
ORDER BY depth DESC
FETCH FIRST 1 ROW ONLY
) AS nth_node
FROM test_data;
对于您的示例数据:
CREATE TABLE test_data ( unique_id,lookup_acct_code ) AS
SELECT '1','leaf-1' FROM DUAL UNION ALL
SELECT '2','stem-2' FROM DUAL UNION ALL
SELECT '3','branch-1' FROM DUAL UNION ALL
SELECT '4','trunk-2' FROM DUAL UNION ALL
SELECT '5','root-1' FROM DUAL;
CREATE TABLE linked_accounts ( acct_code,parent_code ) AS
SELECT 'leaf-1','stem-1' FROM DUAL UNION ALL
SELECT 'stem-1','twig-1' FROM DUAL UNION ALL
SELECT 'twig-1','stick-1' FROM DUAL UNION ALL
SELECT 'stick-1','branch-1' FROM DUAL UNION ALL
SELECT 'branch-1','trunk-1' FROM DUAL UNION ALL
SELECT 'trunk-1','root-1' FROM DUAL UNION ALL
SELECT 'root-1',NULL FROM DUAL UNION ALL
SELECT 'leaf-2','stem-2' FROM DUAL UNION ALL
SELECT 'stem-2','twig-2' FROM DUAL UNION ALL
SELECT 'twig-2','stick-2' FROM DUAL UNION ALL
SELECT 'stick-2','branch-2' FROM DUAL UNION ALL
SELECT 'branch-2','trunk-2' FROM DUAL UNION ALL
SELECT 'trunk-2','root-2' FROM DUAL UNION ALL
SELECT 'root-2',NULL FROM DUAL;
两个输出:
UNIQUE_ID | NTH_FROM_ROOT :-------- | :------------ 1 | branch-1 2 | branch-2 3 | branch-1 4 | trunk-2 5 | root-1
您需要在更大的数据集上分析所有解决方案,以查看哪个解决方案的性能更高。
dbfiddle here
,您可以尝试通过使用 CONNECT_BY_ROOT
函数来获取您已开始的 unique_id
来避免标量子查询。并根据构建树后找到的最大级别进行相同的过滤。试试这个:
with hier as (
select
/*Find unique ID of subtree*/
connect_by_root(f.unique_id) as unique_id,connect_by_root(la.acct_code) as start_node,la.acct_code,/*Find max depth of subtree*/
max(level) over(partition by connect_by_root(f.unique_id)) as max_lvl,/*Find current reversed depth of node*/
level as lvl
from linked_accounts la
left join test_data f
on la.acct_code = f.lookup_acct_code
start with f.unique_id is not null
connect by prior la.parent_code = la.acct_code
)
select
unique_id,case max_lvl
when lvl
/*Select our start node for short subtree*/
then start_node
/*And node al level 3 otherwise*/
else acct_code
end as node_name
from hier
/*Select the node on level 3 from the root*/
where max_lvl - lvl = 2
/*Or the last node in subtree for the depth < 3*/
or (max_lvl <= 2 and lvl = max_lvl)
order by 1 asc
UNIQUE_ID | NODE_NAME :-------- | :-------- 1 | branch-1 2 | branch-2 3 | branch-1 4 | trunk-2 5 | root-1
dbfiddle here
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。