我想获得有关分层查询(Oracle 11gR2)的帮助。这些查询让我很难受...
实际上,这是2合1问题(需要2种不同的方法)。
我正在寻找一种方法来获取所有个人记录到根的距离(不是相反的)。我的数据位于树状结构中:
CREATE TABLE MY_TREE
(ID_NAME VARCHAR2(1) PRIMARY KEY,
PARENT_ID VARCHAR2(1),
PARENT_DISTANCE NUMBER(2)
);
INSERT INTO MY_TREE (ID_NAME,PARENT_ID,PARENT_DISTANCE) VALUES('A',NULL,NULL);
INSERT INTO MY_TREE (ID_NAME,PARENT_ID,PARENT_DISTANCE) VALUES('B','A',1);
INSERT INTO MY_TREE (ID_NAME,PARENT_ID,PARENT_DISTANCE) VALUES('C','B',3);
INSERT INTO MY_TREE (ID_NAME,PARENT_ID,PARENT_DISTANCE) VALUES('D','B',5);
INSERT INTO MY_TREE (ID_NAME,PARENT_ID,PARENT_DISTANCE) VALUES('E','C',7);
INSERT INTO MY_TREE (ID_NAME,PARENT_ID,PARENT_DISTANCE) VALUES('F','D',11);
INSERT INTO MY_TREE (ID_NAME,PARENT_ID,PARENT_DISTANCE) VALUES('G','D',13);
从层次上讲,我的数据如下所示(但是我有多个独立的根,并且还有更多的层次):
在第一个方法中,我正在寻找一个查询,该查询将给出以下结果:
LEVEL ROOT NODE ID_NAME ROOT_DISTANCE
----- ---- ---- ------- -------------
1 A null A null
2 A null B 1
3 A B C 4
4 A B E 11
3 A B D 6
4 A D F 17
4 A D G 19
在此结果中,
在此方法中,我将始终指定最多2个根。
第二个方法必须是PL / SQL脚本,该脚本将执行相同的计算(ROOT_DISTANCE),但以迭代方式进行,并将结果写入新表中。我想一次运行此脚本,因此将处理所有根(〜1000)。
这是我看脚本的方式:
“性能角度”需要此脚本,因此,如果已经计算出一个元素(例如:由另一片叶子计算出的分割节点),则需要停止计算并传递到下一个叶子,因为我们已经知道从那里到根的结果。例如,如果系统先计算E-C-B-A,然后计算F-D-B-A,则不应再次计算B-A部分,因为它是在第一遍中完成的。
您可以对这两个问题中的一个或两个都加篷,但我需要为这两个问题加篷。
谢谢!
答案 0 :(得分:3)
尝试这个:
WITH brumba(le_vel,root,node,id_name,root_distance) AS (
SELECT 1 as le_vel, id_name as root, null as node, id_name, to_number(null) as root_distance
FROM MY_TREE WHERE parent_id IS NULL
UNION ALL
SELECT b.le_vel + 1, b.root,
CASE WHEN 1 < (
SELECT count(*) FROM MY_TREE t1 WHERE t1.parent_id = t.parent_id
)
THEN t.parent_id ELSE b.node
END,
t.id_name, coalesce(b.root_distance,0)+t.parent_distance
FROM MY_TREE t
JOIN brumba b ON b.id_name = t.parent_id
)
SELECT * FROM brumba
演示:https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=d5c231055e989c3cbcd763f4b3d3033f
不需要使用PL / SQL进行“第二次处理”-上面的SQL将立即计算所有根节点(在parent_id
列中为空)的结果。
只需在上述查询中添加INSERT INTO tablename(col1,col2, ... colN) ...
或CREATE TABLE name AS ...
前缀即可。
上面的演示包含后一个选项CREATE TABLE xxx AS query
答案 1 :(得分:1)
以下是一个显示如何获得问题第一部分的选项:
SQL> with temp as
2 (select level lvl,
3 ltrim(sys_connect_by_path(id_name, ','), ',') path
4 from my_tree
5 start with parent_id is null
6 connect by prior id_name = parent_id
7 ),
8 inter as
9 (select t.lvl,
10 t.path,
11 regexp_substr(t.path, '[^,]+', 1, column_value) col
12 from temp t,
13 table(cast(multiset(select level from dual
14 connect by level <= regexp_count(path, ',') + 1
15 ) as sys.odcinumberlist ))
16 )
17 select i.lvl,
18 i.path,
19 sum(m.parent_distance) dis
20 from inter i join my_tree m on m.id_name = i.col
21 group by i.lvl, i.path
22 order by i.path;
LVL PATH DIS
---- ---------- ----------
1 A
2 A,B 1
3 A,B,C 4
4 A,B,C,E 11
3 A,B,D 6
4 A,B,D,F 17
4 A,B,D,G 19
7 rows selected.
SQL>
答案 2 :(得分:0)
这是如何通过分层(connect by
)查询来解决此问题的方法。
在大多数分层问题中,分层查询将比递归查询(递归with
子句)更快。但是,您的问题并非纯粹是分层的-您需要计算到根的距离,并且与递归with
不同,对于分层查询,您无法一次通过。因此,听到您的来信将会很有趣! -两种方法之间是否存在明显的性能差异。就其价值而言,在您提供的非常小的数据样本上,优化器使用connect by
估算的成本为5,而递归解决方案的成本为48;这是否意味着您将发现现实生活中的任何东西,并希望您也能让我们知道。
在分层查询中,我标记出有两个或更多孩子的父母(我为此使用了解析函数,以避免联接)。然后,我构建层次结构,并在最后一步中进行汇总以获取所需的位。与递归解决方案一样,这应该在单个SQL查询中为您提供所有所需的一切-对于所有根和所有节点;不需要PL / SQL代码。
with
branching (id_name, parent_id, parent_distance, b) as (
select id_name, parent_id, parent_distance,
case when count(*) over (partition by parent_id) > 1 then 'y' end
from my_tree
)
, hierarchy (lvl, leaf, id_name, parent_id, parent_distance, b) as (
select level, connect_by_root id_name, id_name, parent_id, parent_distance, b
from branching
connect by id_name = prior parent_id
)
select max(lvl) as lvl,
min(id_name) keep (dense_rank last order by lvl) as root,
leaf as id_name,
min(decode(b, 'y', parent_id))
keep (dense_rank first order by decode(b, 'y', lvl)) as node,
sum(parent_distance) as root_distance
from hierarchy
group by leaf;
LVL ROOT ID_NAME NODE ROOT_DISTANCE
--- ------- ------- ------- -------------
1 A A
2 A B 1
3 A C B 4
3 A D B 6
4 A E B 11
4 A F D 17
4 A G D 19