Search code examples
sqloracle12chierarchical-data

Hierarchical SQL as linked list


I have a data set that represents a set of process steps to create a set of products. Each product has a step sequence defined in the data set, as FromStep and ToStep. I'm trying to use a hierarchical query to pull the process steps for all products but I'm clearly missing something because it's not working. Any advice on where I've gone wrong would be hugely appreciated (perhaps this isn't even the way to approach this task). I've tried to create a minimal example of my problem below - my real data set is much larger than this.

Create a sample table:

CREATE TABLE HIER_TEST    
(      
    PRODUCT     VARCHAR2(26 BYTE),    
    STEPNAME    VARCHAR2(26 BYTE),    
    STEPID      NUMBER(4,0),    
    FROMSTEP    NUMBER(4,0),    
    TOSTEP      NUMBER(4,0)   
) ;   

Add some data:

Insert into HIER_TEST (PRODUCT,STEPNAME,STEPID,FROMSTEP,TOSTEP) values ('Product1','Step1',1,1,2);   
Insert into HIER_TEST (PRODUCT,STEPNAME,STEPID,FROMSTEP,TOSTEP) values ('Product1','Step2',2,2,3);   
Insert into HIER_TEST (PRODUCT,STEPNAME,STEPID,FROMSTEP,TOSTEP) values ('Product1','Step3',3,3,4);   
Insert into HIER_TEST (PRODUCT,STEPNAME,STEPID,FROMSTEP,TOSTEP) values ('Product1','Step4',4,4,5);   
Insert into HIER_TEST (PRODUCT,STEPNAME,STEPID,FROMSTEP,TOSTEP) values ('Product1','Step5',5,5,6);   
Insert into HIER_TEST (PRODUCT,STEPNAME,STEPID,FROMSTEP,TOSTEP) values ('Product2','Step1',1,1,2);   
Insert into HIER_TEST (PRODUCT,STEPNAME,STEPID,FROMSTEP,TOSTEP) values ('Product2','Step2',2,2,3);   
Insert into HIER_TEST (PRODUCT,STEPNAME,STEPID,FROMSTEP,TOSTEP) values ('Product2','Step3',3,3,4);   
Insert into HIER_TEST (PRODUCT,STEPNAME,STEPID,FROMSTEP,TOSTEP) values ('Product2','Step4',4,4,5);   
Insert into HIER_TEST (PRODUCT,STEPNAME,STEPID,FROMSTEP,TOSTEP) values ('Product2','Step5',5,5,6);   
Insert into HIER_TEST (PRODUCT,STEPNAME,STEPID,FROMSTEP,TOSTEP) values ('Product5','Step1',1,1,2);   
Insert into HIER_TEST (PRODUCT,STEPNAME,STEPID,FROMSTEP,TOSTEP) values ('Product5','Step2',2,2,3);   
Insert into HIER_TEST (PRODUCT,STEPNAME,STEPID,FROMSTEP,TOSTEP) values ('Product5','Step3',3,3,4);   
Insert into HIER_TEST (PRODUCT,STEPNAME,STEPID,FROMSTEP,TOSTEP) values ('Product5','Step4',4,4,5);   
Insert into HIER_TEST (PRODUCT,STEPNAME,STEPID,FROMSTEP,TOSTEP) values ('Product5','Step5',5,5,6);   

I'm then trying this query:

select    
    level,   
    connect_by_isleaf leafnode,   
    hier_test.*,   
    connect_by_root stepid as rootItem   
from   
    hier_test       
start with   
    stepid = 1   
connect by nocycle prior   
    tostep = fromstep   
order siblings by   
    product,   
    stepid   
;   

which returns:

+-------+-------+--------------+-----------+-------+-------+-------+-------+   
| Level | LfNd  |   Product    | StepName  | StepId| FrStp | ToStp | rtItm |   
+-------+-------+--------------+-----------+-------+-------+-------+-------+   
|   1   |   0   |   Product1   |   Step1   |   1   |   1   |   2   |   1   |   
|   2   |   0   |   Product1   |   Step2   |   2   |   2   |   3   |   1   |   
|   3   |   0   |   Product1   |   Step3   |   3   |   3   |   4   |   1   |   
|   4   |   0   |   Product1   |   Step4   |   4   |   4   |   5   |   1   |   
|   5   |   1   |   Product1   |   Step5   |   5   |   5   |   6   |   1   |   
|   5   |   1   |   Product2   |   Step5   |   5   |   5   |   6   |   1   |   
|   5   |   1   |   Product5   |   Step5   |   5   |   5   |   6   |   1   |   
|   4   |   0   |   Product2   |   Step4   |   4   |   4   |   5   |   1   |   
|   5   |   1   |   Product1   |   Step5   |   5   |   5   |   6   |   1   |   
|   5   |   1   |   Product2   |   Step5   |   5   |   5   |   6   |   1   |   
|   5   |   1   |   Product5   |   Step5   |   5   |   5   |   6   |   1   |   
|   4   |   0   |   Product5   |   Step4   |   4   |   4   |   5   |   1   |   
|   5   |   1   |   Product1   |   Step5   |   5   |   5   |   6   |   1   |   
|   5   |   1   |   Product2   |   Step5   |   5   |   5   |   6   |   1   |   
|   5   |   1   |   Product5   |   Step5   |   5   |   5   |   6   |   1   |   
|   3   |   0   |   Product2   |   Step3   |   3   |   3   |   4   |   1   |   
|   4   |   0   |   Product1   |   Step4   |   4   |   4   |   5   |   1   |   
|   5   |   1   |   Product1   |   Step5   |   5   |   5   |   6   |   1   |   
|   5   |   1   |   Product2   |   Step5   |   5   |   5   |   6   |   1   |   
|   5   |   1   |   Product5   |   Step5   |   5   |   5   |   6   |   1   |   

This appears to be locked in a loop forever...

I was expecting the order siblings by clause to sort the table by product then step number, but the results aren't showing this.

What I was aiming to get was this:

+-------+-------+--------------+-----------+-------+-------+-------+-------+   
| Level | LfNd  |   Product    | StepName  | StepId| FrStp | ToStp | rtItm |   
+-------+-------+--------------+-----------+-------+-------+-------+-------+   
|   1   |   0   |   Product1   |   Step1   |   1   |   1   |   2   |   1   |   
|   2   |   0   |   Product1   |   Step2   |   2   |   2   |   3   |   1   |   
|   3   |   0   |   Product1   |   Step3   |   3   |   3   |   4   |   1   |   
|   4   |   0   |   Product1   |   Step4   |   4   |   4   |   5   |   1   |   
|   5   |   1   |   Product1   |   Step5   |   5   |   5   |   6   |   1   |   
|   1   |   0   |   Product2   |   Step1   |   1   |   1   |   2   |   1   |   
|   2   |   0   |   Product2   |   Step2   |   2   |   2   |   3   |   1   |   
|   3   |   0   |   Product2   |   Step3   |   3   |   3   |   4   |   1   |   
|   4   |   0   |   Product2   |   Step4   |   4   |   4   |   5   |   1   |   
|   5   |   1   |   Product2   |   Step5   |   5   |   5   |   6   |   1   |   
|   1   |   0   |   Product5   |   Step1   |   1   |   1   |   2   |   1   |   
|   2   |   0   |   Product5   |   Step2   |   2   |   2   |   3   |   1   |   
|   3   |   0   |   Product5   |   Step3   |   3   |   3   |   4   |   1   |   
|   4   |   0   |   Product5   |   Step4   |   4   |   4   |   5   |   1   |   
|   5   |   1   |   Product5   |   Step5   |   5   |   5   |   6   |   1   |   

I'm sure this will be obvious but I've found nothing that really helps me when trying to find a solution.

Thanks in advance.


Solution

  • The results are showing it.

    CREATE TABLE HIER_TEST    
      (      
      PRODUCT     VARCHAR2(26 BYTE),    
      STEPNAME    VARCHAR2(26 BYTE),    
      STEPID      NUMBER(4,0),    
      FROMSTEP    NUMBER(4,0),    
      TOSTEP      NUMBER(4,0)   
    );
    

    Taking a smaller sub-set of your data so it is easier to visualise:

    Insert into HIER_TEST (PRODUCT,STEPNAME,STEPID,FROMSTEP,TOSTEP)
    SELECT 'Product1','Step1',1,1,2 FROM DUAL UNION ALL   
    SELECT 'Product1','Step2',2,2,3 FROM DUAL UNION ALL   
    SELECT 'Product1','Step3',3,3,4 FROM DUAL UNION ALL
    SELECT 'Product2','Step1',1,1,2 FROM DUAL UNION ALL   
    SELECT 'Product2','Step2',2,2,3 FROM DUAL UNION ALL   
    SELECT 'Product2','Step3',3,3,4 FROM DUAL UNION ALL
    SELECT 'Product5','Step1',1,1,2 FROM DUAL UNION ALL   
    SELECT 'Product5','Step2',2,2,3 FROM DUAL UNION ALL   
    SELECT 'Product5','Step3',3,3,4 FROM DUAL;
    

    Then your query with an added SYS_CONNECT_BY_PATH( Product, ', ' ) (you do not need the NOCYCLE clause as there aren't any cycles in your data):

    select level,   
           connect_by_isleaf leaf,
           product,
           stepname AS sname,
           stepid AS id,
           fromstep AS fs,
           tostep AS ts,
           connect_by_root stepid as rt,
           SYS_CONNECT_BY_PATH( product, ', ' ) As path
    from   hier_test       
    start with
           stepid = 1   
    connect by
           prior tostep = fromstep   
    order siblings by   
            product,   
            stepid
    

    Gives the output:

    LEVEL | LEAF | PRODUCT  | SNAME | ID | FS | TS | RT | PATH                          
    ----: | ---: | :------- | :---- | -: | -: | -: | -: | :-----------------------------
        1 |    0 | Product1 | Step1 |  1 |  1 |  2 |  1 | , Product1                    
        2 |    0 | Product1 | Step2 |  2 |  2 |  3 |  1 | , Product1, Product1          
        3 |    1 | Product1 | Step3 |  3 |  3 |  4 |  1 | , Product1, Product1, Product1
        3 |    1 | Product2 | Step3 |  3 |  3 |  4 |  1 | , Product1, Product1, Product2
        3 |    1 | Product5 | Step3 |  3 |  3 |  4 |  1 | , Product1, Product1, Product5
        2 |    0 | Product2 | Step2 |  2 |  2 |  3 |  1 | , Product1, Product2          
        3 |    1 | Product1 | Step3 |  3 |  3 |  4 |  1 | , Product1, Product2, Product1
        3 |    1 | Product2 | Step3 |  3 |  3 |  4 |  1 | , Product1, Product2, Product2
        3 |    1 | Product5 | Step3 |  3 |  3 |  4 |  1 | , Product1, Product2, Product5
        2 |    0 | Product5 | Step2 |  2 |  2 |  3 |  1 | , Product1, Product5          
        3 |    1 | Product1 | Step3 |  3 |  3 |  4 |  1 | , Product1, Product5, Product1
        3 |    1 | Product2 | Step3 |  3 |  3 |  4 |  1 | , Product1, Product5, Product2
        3 |    1 | Product5 | Step3 |  3 |  3 |  4 |  1 | , Product1, Product5, Product5
        1 |    0 | Product2 | Step1 |  1 |  1 |  2 |  1 | , Product2                    
        2 |    0 | Product1 | Step2 |  2 |  2 |  3 |  1 | , Product2, Product1          
        3 |    1 | Product1 | Step3 |  3 |  3 |  4 |  1 | , Product2, Product1, Product1
        3 |    1 | Product2 | Step3 |  3 |  3 |  4 |  1 | , Product2, Product1, Product2
        3 |    1 | Product5 | Step3 |  3 |  3 |  4 |  1 | , Product2, Product1, Product5
        2 |    0 | Product2 | Step2 |  2 |  2 |  3 |  1 | , Product2, Product2          
        3 |    1 | Product1 | Step3 |  3 |  3 |  4 |  1 | , Product2, Product2, Product1
        3 |    1 | Product2 | Step3 |  3 |  3 |  4 |  1 | , Product2, Product2, Product2
        3 |    1 | Product5 | Step3 |  3 |  3 |  4 |  1 | , Product2, Product2, Product5
        2 |    0 | Product5 | Step2 |  2 |  2 |  3 |  1 | , Product2, Product5          
        3 |    1 | Product1 | Step3 |  3 |  3 |  4 |  1 | , Product2, Product5, Product1
        3 |    1 | Product2 | Step3 |  3 |  3 |  4 |  1 | , Product2, Product5, Product2
        3 |    1 | Product5 | Step3 |  3 |  3 |  4 |  1 | , Product2, Product5, Product5
        1 |    0 | Product5 | Step1 |  1 |  1 |  2 |  1 | , Product5                    
        2 |    0 | Product1 | Step2 |  2 |  2 |  3 |  1 | , Product5, Product1          
        3 |    1 | Product1 | Step3 |  3 |  3 |  4 |  1 | , Product5, Product1, Product1
        3 |    1 | Product2 | Step3 |  3 |  3 |  4 |  1 | , Product5, Product1, Product2
        3 |    1 | Product5 | Step3 |  3 |  3 |  4 |  1 | , Product5, Product1, Product5
        2 |    0 | Product2 | Step2 |  2 |  2 |  3 |  1 | , Product5, Product2          
        3 |    1 | Product1 | Step3 |  3 |  3 |  4 |  1 | , Product5, Product2, Product1
        3 |    1 | Product2 | Step3 |  3 |  3 |  4 |  1 | , Product5, Product2, Product2
        3 |    1 | Product5 | Step3 |  3 |  3 |  4 |  1 | , Product5, Product2, Product5
        2 |    0 | Product5 | Step2 |  2 |  2 |  3 |  1 | , Product5, Product5          
        3 |    1 | Product1 | Step3 |  3 |  3 |  4 |  1 | , Product5, Product5, Product1
        3 |    1 | Product2 | Step3 |  3 |  3 |  4 |  1 | , Product5, Product5, Product2
        3 |    1 | Product5 | Step3 |  3 |  3 |  4 |  1 | , Product5, Product5, Product5
    

    Then looking at the path, you can see that it is ordered by the sibling at the first level, then at the second level and then at the third level. It is becuse you are only looking at the product (etc) at the current depth that you are not seeing this ordering.

    If you want to restrict it to having the same product as at the previous level then add that to the CONNECT BY clause:

    select level,   
           connect_by_isleaf leaf,
           product,
           stepname AS sname,
           stepid AS id,
           fromstep AS fs,
           tostep AS ts,
           connect_by_root stepid as rt,
           SYS_CONNECT_BY_PATH( product, ', ' ) As path
    from   hier_test       
    start with
           stepid = 1   
    connect by
           prior tostep  = fromstep
    and    prior product = product
    order siblings by   
            product,   
            stepid
    

    Which outputs:

    LEVEL | LEAF | PRODUCT  | SNAME | ID | FS | TS | RT | PATH                          
    ----: | ---: | :------- | :---- | -: | -: | -: | -: | :-----------------------------
        1 |    0 | Product1 | Step1 |  1 |  1 |  2 |  1 | , Product1                    
        2 |    0 | Product1 | Step2 |  2 |  2 |  3 |  1 | , Product1, Product1          
        3 |    1 | Product1 | Step3 |  3 |  3 |  4 |  1 | , Product1, Product1, Product1
        1 |    0 | Product2 | Step1 |  1 |  1 |  2 |  1 | , Product2                    
        2 |    0 | Product2 | Step2 |  2 |  2 |  3 |  1 | , Product2, Product2          
        3 |    1 | Product2 | Step3 |  3 |  3 |  4 |  1 | , Product2, Product2, Product2
        1 |    0 | Product5 | Step1 |  1 |  1 |  2 |  1 | , Product5                    
        2 |    0 | Product5 | Step2 |  2 |  2 |  3 |  1 | , Product5, Product5          
        3 |    1 | Product5 | Step3 |  3 |  3 |  4 |  1 | , Product5, Product5, Product5
    

    db<>fiddle here