如何尽可能高效地在 Oracle SQL 中构建层次结构
How to build a hierarchy in Oracle SQL as efficiently as possible
这是在 Oracle SQL 中使用他们的 BI Publisher 数据模型工具
假设你有两个 table:
hierarchy_table
acct_description_table
在这两个 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, tree.parentkey parent, act.description desc
from hierarchy_table tree, 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
操作来完成 table。我研究了递归以帮助加快此查询。但是,我不确定如何对其进行编码以满足我们对每个深度对应的列的需求。
有没有人做过类似的事情?或者有谁知道我们可以比当前由 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', '450000', 1 FROM DUAL UNION ALL
SELECT '450051', '450050', 2 FROM DUAL ),
acct_description_table ( acct_value, description ) AS
-- This is test data, you would not have this WITH clause...
( SELECT '450000', '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,':'),'[^:]+',1,2) acct2,
REGEXP_SUBSTR(SYS_CONNECT_BY_PATH(ht.primarykey,':'),'[^:]+',1,3) acct3,
REGEXP_SUBSTR(SYS_CONNECT_BY_PATH(ht.primarykey,':'),'[^:]+',1,4) acct4,
REGEXP_SUBSTR(SYS_CONNECT_BY_PATH(ht.primarykey,':'),'[^:]+',1,5) acct5,
REGEXP_SUBSTR(SYS_CONNECT_BY_PATH(ht.primarykey,':'),'[^:]+',1,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 |
+--------+--------+--------+-------+-------+-------+--------------+
这是在 Oracle SQL 中使用他们的 BI Publisher 数据模型工具
假设你有两个 table:
hierarchy_table
acct_description_table
在这两个 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, tree.parentkey parent, act.description desc
from hierarchy_table tree, 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
操作来完成 table。我研究了递归以帮助加快此查询。但是,我不确定如何对其进行编码以满足我们对每个深度对应的列的需求。
有没有人做过类似的事情?或者有谁知道我们可以比当前由 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', '450000', 1 FROM DUAL UNION ALL
SELECT '450051', '450050', 2 FROM DUAL ),
acct_description_table ( acct_value, description ) AS
-- This is test data, you would not have this WITH clause...
( SELECT '450000', '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,':'),'[^:]+',1,2) acct2,
REGEXP_SUBSTR(SYS_CONNECT_BY_PATH(ht.primarykey,':'),'[^:]+',1,3) acct3,
REGEXP_SUBSTR(SYS_CONNECT_BY_PATH(ht.primarykey,':'),'[^:]+',1,4) acct4,
REGEXP_SUBSTR(SYS_CONNECT_BY_PATH(ht.primarykey,':'),'[^:]+',1,5) acct5,
REGEXP_SUBSTR(SYS_CONNECT_BY_PATH(ht.primarykey,':'),'[^:]+',1,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 | +--------+--------+--------+-------+-------+-------+--------------+