如何在Oracle SQL中尽可能高效地构建层次结构

问题描述

这是在Oracle sql中使用其BI Publisher数据模型工具

假设您有两个表:

hierarchy_table acct_description_table

在这两个表中,您具有以下列:

hierarchy_table
+------------+-----------+-------+
| primarykey | parentkey | depth |
+------------+-----------+-------+

acct_description_table
+------------+-------------+
| acct_value | description |
+------------+-------------+

并使用此数据来构建这样的层次结构(省略说明):

+----------------+----------------+----------------+
|  acct_depth_0  |  acct_depth_1  |  acct_depth_2  | . . .
+----------------+----------------+----------------+
|     450000     |     450040     |                | . . .
+----------------+----------------+----------------+
|     450000     |     450050     |     450051     | . . .
+----------------+----------------+----------------+

实现此目标的最佳方法是什么?到目前为止,我有以下sql

select distinct
  t1.acct,t1.desc,t2.acct,t2.desc,t3.acct,t3.desc,t4.acct,t4.desc,t5.acct,t5.desc,t6.acct,t6.desc
from (select tree.primarykey acct,act.description desc 
      from hierarchy_table tree,acct_description_table act 
      where 1=1
        and tree.depth = 0
        and act.acct_value = tree.primarykey
     ) t1
left join (select tree.primarykey acct,tree.parentkey parent,act.description desc 
           from hierarchy_table tree,acct_description_table act 
           where 1=1
             and tree.depth = 1
             and act.acct_value = tree.primarykey
     ) t2
  on 1=1 and t1.acct = t2.parent
...
...
...
left join (select tree.primarykey acct,acct_description_table act 
           where 1=1
             and tree.depth = 5
             and act.acct_value = tree.primarykey
     ) t6
  on 1=1 and t5.acct = t6.parent

如您所见,通过这样的查询,我们将执行5种不同的left join操作来完成表格。我研究了递归来帮助加快查询速度。但是,我不确定如何对它进行编码以符合我们需要与每个深度对应的列的需求。

有人做过类似的事情吗?还是有人知道我们可以比包含5个不同左联接的当前查询更快地执行此操作?谢谢!

解决方法

Oracle的CONNECT BY功能非常适合层次结构,并且非常高效。这是您将其用于数据的一种方式:

with hierarchy_table ( primarykey,parentkey,depth ) as 
-- This is test data,you would not have this WITH clause...
( SELECT '450000',null,0 FROM DUAL UNION ALL
  SELECT '450040','450000',1 FROM DUAL UNION ALL
  SELECT '450050',1 FROM DUAL UNION ALL  
  SELECT '450051','450050',2 FROM DUAL ),acct_description_table ( acct_value,description ) AS
-- This is test data,'Acct #450000' FROM DUAL UNION ALL
  SELECT '450040','Acct #450040' FROM DUAL UNION ALL
  SELECT '450050','Acct #450050' FROM DUAL UNION ALL  
  SELECT '450051','Acct #450051' FROM DUAL )
-- Real query starts here
SELECT REGEXP_SUBSTR(SYS_CONNECT_BY_PATH(ht.primarykey,':'),'[^:]+',1,1) acct1,REGEXP_SUBSTR(SYS_CONNECT_BY_PATH(ht.primarykey,2) acct2,3) acct3,4) acct4,5) acct5,6) acct6,adt.description
FROM    hierarchy_table ht
INNER JOIN acct_description_table adt ON adt.acct_value = ht.primarykey
WHERE CONNECT_BY_ISLEAF = 1
START WITH ht.parentkey IS NULL
CONNECT BY ht.parentkey = PRIOR ht.primarykey
AND ht.depth = PRIOR ht.depth + 1  -- There is actually no need for the "DEPTH" column in your table.
AND LEVEL <= 6;
+--------+--------+--------+-------+-------+-------+--------------+
| ACCT1  | ACCT2  | ACCT3  | ACCT4 | ACCT5 | ACCT6 | DESCRIPTION  |
+--------+--------+--------+-------+-------+-------+--------------+
| 450000 | 450040 |        |       |       |       | Acct #450040 |
| 450000 | 450050 | 450051 |       |       |       | Acct #450051 |
+--------+--------+--------+-------+-------+-------+--------------+