Sql 如何在Oracle 10g中解析相邻列表?
我有一张这样的桌子:Sql 如何在Oracle 10g中解析相邻列表?,sql,oracle,oracle10g,hierarchy,common-table-expression,Sql,Oracle,Oracle10g,Hierarchy,Common Table Expression,我有一张这样的桌子: +---------+--------+ | EMP_ID | MGR_iD | +---------+--------+ | 1 | 1 | | 2 | 1 | | 3 | 1 | | 4 | 2 | | 5 | 2 | | 6 | 2 | | 7 | 3 | | 8 | 5 | |
+---------+--------+
| EMP_ID | MGR_iD |
+---------+--------+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 2 |
| 5 | 2 |
| 6 | 2 |
| 7 | 3 |
| 8 | 5 |
| 9 | 7 |
| 10 | 5 |
| 11 | 7 |
| 12 | 9 |
| 13 | 9 |
| 14 | 9 |
+---------+--------+
我正在尝试解析此相邻列表以生成以下结果集:
| EMP_ID | MGR_ID | LV | LEVEL1 | LEVEL2 | LEVEL3 | LEVEL4 | LEVEL5 |
---------------------------------------------------------------------
| 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
| 2 | 1 | 2 | 1 | 2 | 2 | 2 | 2 |
| 3 | 1 | 2 | 1 | 3 | 3 | 3 | 3 |
| 4 | 2 | 3 | 1 | 2 | 4 | 4 | 4 |
| 5 | 2 | 3 | 1 | 2 | 5 | 5 | 5 |
| 6 | 2 | 3 | 1 | 2 | 6 | 6 | 6 |
| 7 | 3 | 3 | 1 | 3 | 7 | 7 | 7 |
| 8 | 5 | 4 | 1 | 2 | 5 | 8 | 8 |
| 9 | 7 | 4 | 1 | 3 | 7 | 9 | 9 |
| 10 | 5 | 4 | 1 | 2 | 5 | 10 | 10 |
| 11 | 7 | 4 | 1 | 3 | 7 | 11 | 11 |
| 12 | 9 | 5 | 1 | 3 | 7 | 9 | 12 |
| 13 | 9 | 5 | 1 | 3 | 7 | 9 | 13 |
| 14 | 9 | 5 | 1 | 3 | 7 | 9 | 14 |
以下是我迄今为止所取得的成就:
create table PC (
EMP_ID NUMBER NULL,
MGR_ID NUMBER NULL
);
INSERT INTO PC (EMP_ID, MGR_ID)
VALUES (1.0, 1.0);
INSERT INTO PC (EMP_ID, MGR_ID)
VALUES (2.0, 1.0);
INSERT INTO PC (EMP_ID, MGR_ID)
VALUES (3.0, 1.0);
INSERT INTO PC (EMP_ID, MGR_ID)
VALUES (4.0, 2.0);
INSERT INTO PC (EMP_ID, MGR_ID)
VALUES (5.0, 2.0);
INSERT INTO PC (EMP_ID, MGR_ID)
VALUES (6.0, 2.0);
INSERT INTO PC (EMP_ID, MGR_ID)
VALUES (7.0, 3.0);
INSERT INTO PC (EMP_ID, MGR_ID)
VALUES (8.0, 5.0);
INSERT INTO PC (EMP_ID, MGR_ID)
VALUES (9.0, 7.0);
INSERT INTO PC (EMP_ID, MGR_ID)
VALUES (10.0, 5.0);
INSERT INTO PC (EMP_ID, MGR_ID)
VALUES (11.0, 7.0);
INSERT INTO PC (EMP_ID, MGR_ID)
VALUES (12.0, 9.0);
INSERT INTO PC (EMP_ID, MGR_ID)
VALUES (13.0, 9.0);
INSERT INTO PC (EMP_ID, MGR_ID)
VALUES (14.0, 9.0);
以及查询:
WITH PERSON_HIER AS
(
SELECT 1 as level1,
CAST(NULL AS NUMBER) as level2,
CAST(NULL AS NUMBER) as level3,
CAST(NULL AS NUMBER) as level4,
CAST(NULL AS NUMBER) as level5
FROM PC
WHERE EMP_ID = 1 AND MGR_ID = 1
UNION ALL
SELECT L1.EMP_ID AS LEVEL1,
L2.EMP_ID AS LEVEL2,
L3.EMP_ID AS LEVEL3,
L4.EMP_ID AS LEVEL4,
L5.EMP_ID AS LEVEL5
FROM PC L1
LEFT OUTER JOIN PC L2 ON (L1.EMP_ID = L2.MGR_ID AND L2.EMP_ID != L1.EMP_ID)
LEFT OUTER JOIN PC L3 ON (L2.EMP_ID = L3.MGR_ID)
LEFT OUTER JOIN PC L4 ON (L3.EMP_ID = L4.MGR_ID)
LEFT OUTER JOIN PC L5 ON (L4.EMP_ID = L5.MGR_ID)
WHERE L1.MGR_ID = L1.EMP_ID
)
SELECT level1,
coalesce(level2, level1) AS LEVEL2,
coalesce(level3, level2, level1) AS LEVEL3,
coalesce(level4, level3, level2, level1) AS LEVEL4,
coalesce(level5, level4, level3, level2, level1) AS LEVEL5
FROM PERSON_HIER
order by level5
我使用的是Oracle 10g,因此在Oracle 10g中不可能使用递归CTE或层次查询。您可以使用以下单个层次查询:
.现在,一个改进的版本显示了正确的层次结构:
select emp_id, mgr_id, lvl, h,
nvl(substr(h,instr(h,'/',1, 2)+1, instr(h, '/',1, 3)- instr(h,'/',1,2)-1), emp_id) as lvl1,
nvl(substr(h,instr(h,'/',1, 3)+1, instr(h, '/',1, 4)- instr(h,'/',1,3)-1), emp_id) as lvl2,
nvl(substr(h,instr(h,'/',1, 4)+1, instr(h, '/',1, 5)- instr(h,'/',1,4)-1), emp_id) as lvl3,
nvl(substr(h,instr(h,'/',1, 5)+1, instr(h, '/',1, 6) -instr(h,'/',1,5)-1), emp_id) as lvl4,
nvl(substr(h,instr(h,'/',1, 6)+1, instr(h, '/',1, 7) -instr(h,'/',1,6)-1), emp_id) as lvl5
from(
select emp_id, mgr_id , level lvl, sys_connect_by_path(mgr_id, '/')||'/' h
from pc
connect by nocycle prior emp_id = mgr_id
start with emp_id = 1
)
order by emp_id;
EMP_ID MGR_ID LVL H LVL1 LVL2 LVL3 LVL4 LVL5
2 1 1 1/1/ 1 2 2 2 2
3 1 1 1/1/ 1 3 3 3 3
4 2 2 2/1/2/ 1 2 4 4 4
5 2 2 2/1/2/ 1 2 5 5 5
6 2 2 2/1/2/ 1 2 6 6 6
7 3 2 3/1/3/ 1 3 7 7 7
8 5 3 5/1/2/5/ 1 2 5 8 8
9 7 3 7/1/3/7/ 1 3 7 9 9
10 5 3 5/1/2/5/ 1 2 5 10 10
11 7 3 7/1/3/7/ 1 3 7 11 11
12 9 4 9/1/3/7/9/ 1 3 7 9 12
13 9 4 9/1/3/7/9/ 1 3 7 9 13
14 9 4 9/1/3/7/9/ 1 3 7 9 14
15 14 5 14/1/3/7/9/14/ 1 3 7 9 14
这是我第一次尝试:
select emp_id, mgr_id, lvl, h,
nvl(substr(h,instr(h,' ',1, 1), instr(h, ' ',1, 2)- instr(h,' ',1,1)), emp_id) as lvl1,
nvl(substr(h,instr(h,' ',1, 2), instr(h, ' ',1, 3)- instr(h,' ',1,2)), emp_id) as lvl2,
nvl(substr(h,instr(h,' ',1, 3), instr(h, ' ',1, 4)- instr(h,' ',1,3)), emp_id) as lvl3,
nvl(substr(h,instr(h,' ',1, 4), instr(h, ' ',1, 5)- instr(h,' ',1,4)), emp_id) as lvl4,
nvl(substr(h,instr(h,' ',1, 5), instr(h, ' ',1, 6) -instr(h,' ',1,5)), emp_id) as lvl5
from(
select emp_id, mgr_id , level lvl, sys_connect_by_path(mgr_id, ' ') h
from pc
connect by nocycle prior emp_id = mgr_id
start with emp_id = 1
)
order by emp_id;
根据您的需求添加联合
17:33:45 HR@oars_sandbox> ed
Wrote file S:\tools\buffer.sql
1 select emp_id,
2 mgr_id,
3 lvl,
4 path,
5 replace(regexp_substr(path, '#[0-9]+', 1, 1), '#') l1,
6 replace(regexp_substr(path, '#[0-9]+', 1, 2), '#') l2,
7 replace(regexp_substr(path, '#[0-9]+', 1, 3), '#') l3,
8 replace(regexp_substr(path, '#[0-9]+', 1, 4), '#') l4,
9 replace(regexp_substr(path, '#[0-9]+', 1, 5), '#') l5
10 from (
11 select emp_id, mgr_id, level lvl, sys_connect_by_path(emp_id, '#') path
12 from pc
13 connect by mgr_id = prior emp_id and level <= 5 and mgr_id <> emp_id
14 start with emp_id = 1
15 order by 3,1
16* )
17:34:29 HR@oars_sandbox> /
EMP_ID MGR_ID LVL PATH L1 L2 L3 L4 L5
---------- ---------- ---------- -------------------- ----- ----- ----- ----- -----
1 1 1 #1 1
2 1 2 #1#2 1 2
3 1 2 #1#3 1 3
4 2 3 #1#2#4 1 2 4
5 2 3 #1#2#5 1 2 5
6 2 3 #1#2#6 1 2 6
7 3 3 #1#3#7 1 3 7
8 5 4 #1#2#5#8 1 2 5 8
9 7 4 #1#3#7#9 1 3 7 9
10 5 4 #1#2#5#10 1 2 5 10
11 7 4 #1#3#7#11 1 3 7 11
12 9 5 #1#3#7#9#12 1 3 7 9 12
13 9 5 #1#3#7#9#13 1 3 7 9 13
14 9 5 #1#3#7#9#14 1 3 7 9 14
14 rows selected.
Elapsed: 00:00:00.03
17:34:32 HR@oars_sandbox>
如果有超过5个级别,会发生什么?我只想输出前5个级别…什么意思,在10g中不可能进行分层查询?连接方式适用于不同的年龄段,为什么要坚持使用CTE?请参见例如此链接:。如果你能帮我连接,我会很高兴的。这是层次结构的深度。很好的想法从我这里获得所有祖先+1@然后它只返回一行,因为子查询只在根上生成emp_id 14。因此子查询似乎不起作用。它在11g XE上运行良好。谢谢,但这似乎会在结果集中产生重复的emp_id:s。哎哟,在改进的版本中,我删除了记录1,1是多余的。我已经纠正了这一点,这是为了测试目的。见
17:33:45 HR@oars_sandbox> ed
Wrote file S:\tools\buffer.sql
1 select emp_id,
2 mgr_id,
3 lvl,
4 path,
5 replace(regexp_substr(path, '#[0-9]+', 1, 1), '#') l1,
6 replace(regexp_substr(path, '#[0-9]+', 1, 2), '#') l2,
7 replace(regexp_substr(path, '#[0-9]+', 1, 3), '#') l3,
8 replace(regexp_substr(path, '#[0-9]+', 1, 4), '#') l4,
9 replace(regexp_substr(path, '#[0-9]+', 1, 5), '#') l5
10 from (
11 select emp_id, mgr_id, level lvl, sys_connect_by_path(emp_id, '#') path
12 from pc
13 connect by mgr_id = prior emp_id and level <= 5 and mgr_id <> emp_id
14 start with emp_id = 1
15 order by 3,1
16* )
17:34:29 HR@oars_sandbox> /
EMP_ID MGR_ID LVL PATH L1 L2 L3 L4 L5
---------- ---------- ---------- -------------------- ----- ----- ----- ----- -----
1 1 1 #1 1
2 1 2 #1#2 1 2
3 1 2 #1#3 1 3
4 2 3 #1#2#4 1 2 4
5 2 3 #1#2#5 1 2 5
6 2 3 #1#2#6 1 2 6
7 3 3 #1#3#7 1 3 7
8 5 4 #1#2#5#8 1 2 5 8
9 7 4 #1#3#7#9 1 3 7 9
10 5 4 #1#2#5#10 1 2 5 10
11 7 4 #1#3#7#11 1 3 7 11
12 9 5 #1#3#7#9#12 1 3 7 9 12
13 9 5 #1#3#7#9#13 1 3 7 9 13
14 9 5 #1#3#7#9#14 1 3 7 9 14
14 rows selected.
Elapsed: 00:00:00.03
17:34:32 HR@oars_sandbox>