Oracle SQL:如何获取树中根节点和任何节点之间的第 n 个节点优化中

如何解决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 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-