Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to parse adjacent list in Oracle 10g?

I have a table like this:

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

I'm trying to parse this adjacent list to produce a following result set:

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

Here is what I have managed to get so far:

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

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

I'm using Oracle 10g so Recursive CTE or Hierarchial queries not possible in Oracle 10g

like image 653
jrara Avatar asked Dec 13 '12 08:12

jrara


People also ask

What is the difference between hard parse and soft parse?

To review, Oracle hard parsing reads in statistics, utilizes index information and creates an execution plan. Soft parsing already has the execution plan and doesn't need to revisit the statisticsand so on.

Can we use regex in Oracle SQL query?

In Oracle Database 10g, you can use both SQL and PL/SQL to implement regular expression support. Regular expressions are a method of describing both simple and complex patterns for searching and manipulating. String manipulation and searching contribute to a large percentage of the logic in a Web-based application.

What is soft parse in Oracle database?

Description. A soft parse is recorded when the Oracle Server checks the shared pool for a SQL statement and finds a version of the statement that it can reuse. This metric represents the percentage of parse requests where the cursor was already in the cursor cache compared to the number of total parses.

What is REGEXP_LIKE?

REGEXP_LIKE is similar to the LIKE condition, except REGEXP_LIKE performs regular expression matching instead of the simple pattern matching performed by LIKE . This condition evaluates strings using characters as defined by the input character set.


2 Answers

You can use a single hierarchical query using 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.

like image 60
Vincent Malgrat Avatar answered Oct 16 '22 15:10

Vincent Malgrat


Now an improved version(shows correct 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

This is my first try:

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

like image 3
Florin stands with Ukraine Avatar answered Oct 16 '22 15:10

Florin stands with Ukraine