2012-12-13 35 views
7

Ho una tabella come questa:Come analizzare la lista adiacente in Oracle 10g?

+---------+--------+ 
| 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 | 
+---------+--------+ 

sto cercando di analizzare questa lista adiacente a produrre un set di risultati seguente:

| 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 | 

Ecco quello che sono riuscito a ottenere finora:

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); 

e la query:

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 

che sto utilizzando Oracle 10g in modo ricorsivo CTE o query gerarchica non è possibile in Oracle 10g

+0

Cosa succede se ci sono più di 5 livelli? – Ben

+0

Voglio solo produrre i primi 5 livelli ... – jrara

+2

cosa intendi, le query gerarchiche non sono possibili in 10g? 'connect by' è disponibile da anni, perché attenersi a CTE? –

risposta

3

Ora una versione migliorata (mostra corretta hierarcy):

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 

SQL FIDDLE

Questo è il mio primo tentativo:

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; 

See SQLFiddle here

+1

lol, ho pensato che 'nocycle' è apparso in 11g e non lo ha usato ... –

+0

Grazie, ma questo sembra produrre duplicato emp_id: s nel set di risultati. – jrara

+0

Ouch, nella versione migliorata ho rimosso il record (1, 1) che è ridondante. –

5

È possibile utilizzare una singola query gerarchica con CONNECT BY:

SQL> SELECT root, 
    2   MAX(DECODE(lvl, max_lvl, mgr_id)) lvl1, 
    3   MAX(DECODE(lvl, max_lvl, emp_id)) lvl2, 
    4   MAX(DECODE(lvl, greatest(max_lvl-1, 1), emp_id)) lvl3, 
    5   MAX(DECODE(lvl, greatest(max_lvl-2, 1), emp_id)) lvl4, 
    6   MAX(DECODE(lvl, greatest(max_lvl-3, 1), emp_id)) lvl5 
    7 FROM (SELECT connect_by_root(emp_id) root, 
    8     emp_id, 
    9     mgr_id, 
10     level lvl, 
11     MAX (level) 
12      OVER (PARTITION BY connect_by_root(emp_id)) max_lvl 
13   FROM pc 
14   CONNECT BY NOCYCLE PRIOR mgr_id = emp_id) v 
15 GROUP BY root 
16 ORDER BY 1; 

ROOT LVL1 LVL2 LVL3 LVL4 LVL5 
---- ----- ----- ----- ----- ----- 
    1  1  1  1  1  1 
    2  1  2  2  2  2 
    3  1  3  3  3  3 
    4  1  2  4  4  4 
    5  1  2  5  5  5 
    6  1  2  6  6  6 
    7  1  3  7  7  7 
    8  1  2  5  8  8 
    9  1  3  7  9  9 
    10  1  2  5 10 10 
    11  1  3  7 11 11 
    12  1  3  7  9 12 
    13  1  3  7  9 13 
    14  1  3  7  9 14 

SQLFiddle demo.

+0

bel ideea per ottenere tutti gli antenati +1 da me! –

+0

Dovrebbe funzionare su Oracle 10g Express Edition? – jrara

+0

Questo non sembra funzionare su 10g Express Edition. – jrara

3

Aggiungi fondono secondo la vostra richiesta.

17:33:45 [email protected]_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 [email protected]_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 [email protected]_sandbox>