Oracle Tree 树高级应用

本文系转载,如果有侵犯您权益的地方,烦请即使告知

1、一个比较难的SQL(Oracle 9i 实现 10G 的 CONNECT_BY_ISLEAF、CONNECT_BY_ROOT 等功能)

参考网址:http://blog.csdn.net/wzy0623/article/details/1644049

有一个emp表,2个字段,员工id和主管id. 1,emp_id, 2,manager_id
假如有以下资料,一个员工可以对应一个或多个主管id,即一个员工可能有几个主管。
emp_id manager_id
001      101
001      102
101      201
102      202
002      102
003      103
103      203
201      301
203      303

现在要通过任何一个员工id,能查到他的最高主管的id,可能结果不止一笔。

即如果是001,则结果如下:
emp_id manager_id
001      301
001      202
........................
即如果是002,则结果如下:
emp_id manager_id
002      102
........................
即如果是103,则结果如下:
emp_id manager_id
103      303
......................................
以下是建表脚本及资料:

 1 CREATE TABLE emp
 2 (
 3 emp_id VARCHAR2(10 ),
 4 manager_id VARCHAR2(10 )
 5 );
 6 
 7 INSERT INTO emp
 8             (emp_id, manager_id
 9             )
10      VALUES ('001', '101'
11             );
12 INSERT INTO emp
13             (emp_id, manager_id
14             )
15      VALUES ('001', '102'
16             );
17 INSERT INTO emp
18             (emp_id, manager_id
19             )
20      VALUES ('101', '201'
21             );
22 INSERT INTO emp
23             (emp_id, manager_id
24             )
25      VALUES ('102', '202'
26             );
27 INSERT INTO emp
28             (emp_id, manager_id
29             )
30      VALUES ('002', '102'
31             );
32 INSERT INTO emp
33             (emp_id, manager_id
34             )
35      VALUES ('003', '103'
36             );
37 INSERT INTO emp
38             (emp_id, manager_id
39             )
40      VALUES ('103', '203'
41             );
42 INSERT INTO emp
43             (emp_id, manager_id
44             )
45      VALUES ('201', '301'
46             );
47 INSERT INTO emp
48             (emp_id, manager_id
49             )
50      VALUES ('203', '303'
51             );
52 COMMIT ;
View Code

有人给出这样的答案:

1 SELECT emp_id,
2        manager_id
3   FROM (SELECT connect_by_root(emp_id) emp_id,
4                manager_id,
5                connect_by_isleaf v_isleaf
6           FROM emp
7         CONNECT BY emp_id = PRIOR manager_id)
8  WHERE v_isleaf = 1;

这个写法非常简洁,用到了10G connect by 增强的特性,如判断是否叶子节点的伪列 CONNECT_BY_ISLEAF,只使用根行返回结果的一元操作符 CONNECT_BY_ROOT 等,很好。但提问者说使用的是 9i,这就有些麻烦了,能否使用一个 sql 而不是 plsql 实现呢?深入研究后给出了我的 sql:

1 SELECT emp_id, manager_id
2   FROM (SELECT FIRST_VALUE (emp_id) OVER (PARTITION BY part ORDER BY lev) emp_id,
3                ROW_NUMBER () OVER (PARTITION BY part ORDER BY lev DESC) rn,
4                part, manager_id AS manager_id
5         FROM (SELECT emp_id, manager_id, LEVEL lev, (ROWNUM - LEVEL) part
6                 FROM emp
7               CONNECT BY emp_id = PRIOR manager_id))
8 WHERE rn = 1;

9i 没有提供 CONNECT_BY_ISLEAF 及 CONNECT_BY_ROOT,但可以使用分析函数实现其基本功能,下面分析一下。

最内层的查询:

1 SELECT emp_id, manager_id, LEVEL lev, (ROWNUM - LEVEL) part
2 FROM emp
3 CONNECT BY emp_id = PRIOR manager_id;

这里用到了从叶子到跟的反向遍历,同时用 (ROWNUM - LEVEL) part 列的值表示一个从叶子到根的路径,为使用分析函数的分区条件做准备。

二层嵌套查询

 1 SELECT first_value(emp_id) over(PARTITION BY part ORDER BY lev) emp_id,
 2        row_number() over(PARTITION BY part ORDER BY lev DESC) rn,
 3        part,
 4        manager_id AS manager_id
 5   FROM (SELECT emp_id,
 6                manager_id,
 7                LEVEL lev,
 8                (rownum - LEVEL) part
 9           FROM emp
10         CONNECT BY emp_id = PRIOR manager_id);

按 part 分区,以 lev 正排序,再使用 FIRST_VALUE 操作使第一列都显示叶子节点;
按 part 分区,以 lev 倒排序,再使用 ROW_NUMBER () 函数划分等级,等级排第一的即为根节点。

最外层嵌套查询,取得所要求的结果输出。

I believe that we are who we choose to be. Nobody‘s going to come and save you, you‘ve got to save yourself. 我相信我们成为怎样的人是我们自己的选择。没有人会来拯救你,你必须要自己拯救自己。
原文地址:https://www.cnblogs.com/caroline/p/3086152.html