Skip to Main Content
  • Questions
  • build a hierarchy caculation with ONE sql

Breadcrumb

Question and Answer

Connor McDonald

Thanks for the question, Joe.

Asked: July 06, 2017 - 6:37 am UTC

Last updated: July 07, 2017 - 1:01 am UTC

Version: 11.2.0.4

Viewed 1000+ times

You Asked

Hi Team,
We want to build a hierachy process bar with one SQL but failed. We decide to turn to you guys for help.
Plz allow me to put it in this way with a simple test.

>>> Here's my test data:
create table DEMP
(
  ename     VARCHAR2(128), --name
  eid       INTEGER,       -- id
  eparent   INTEGER,       -- parent_id
  eprogress INTEGER        -- progress. If some certain ID has sons, we DO NOT care what this progress value is, this value should be equal to avg of his sons. Else we would take the value as the value of progress. 
)
;



insert into DEMP (ename, eid, eparent, eprogress)
values ('ename1', 1, null, 11);
insert into DEMP (ename, eid, eparent, eprogress)
values ('ename2', 2, 1, 40);
insert into DEMP (ename, eid, eparent, eprogress)
values ('ename3', 3, 1, 60);
insert into DEMP (ename, eid, eparent, eprogress)
values ('ename4', 4, 1, 44);
insert into DEMP (ename, eid, eparent, eprogress)
values ('ename5', 5, 4, 20);
insert into DEMP (ename, eid, eparent, eprogress)
values ('ename6', 6, 4, 80);
insert into DEMP (ename, eid, eparent, eprogress)
values ('ename10', 10, null, 10);
insert into DEMP (ename, eid, eparent, eprogress)
values ('ename11', 11, 10, 100);
insert into DEMP (ename, eid, eparent, eprogress)
values ('ename12', 12, 10, 100);
insert into DEMP (ename, eid, eparent, eprogress)
values ('ename13', 13, 11, 13);
insert into DEMP (ename, eid, eparent, eprogress)
values ('ename14', 14, 13, 1);
insert into DEMP (ename, eid, eparent, eprogress)
values ('ename15', 15, 13, 20);
commit;




>>> our hierachy relationship looks like this:

SQL> select lpad(' ',level*2,' ')||SYS_CONNECT_BY_PATH(ENAME,'-> ') ENAME, eprogress from demp E start with EPARENT is null connect by prior E.EID=E.EPARENT;

ENAME                                                         EPROGRESS
------------------------------------------------------------ ----------
  -> ename1                                                          11 -- value of record is 11, but it should be the average of his sons(40, 60 and 50(average(20 and 80)))
    -> ename1-> ename2                                               40
    -> ename1-> ename3                                               60
    -> ename1-> ename4                                               44  --value of record is 44, but it should be the average of his sons(20 and 80)
      -> ename1-> ename4-> ename5                                    20
      -> ename1-> ename4-> ename6                                    80
  -> ename10                                                         10  --should be the caculated average value of (ename11 and ename12)
    -> ename10-> ename11                                            100  --value of record is 100, but it should be the average of his sons (ename13 only. so is 10.5 means average of (1+20))
      -> ename10-> ename11-> ename13                                 13  --value of record is 13, but it should be the average of his sons(1 and 20)
        -> ename10-> ename11-> ename13-> ename14                      1
        -> ename10-> ename11-> ename13-> ename15                     20
    -> ename10-> ename12                                            100  --100




I mean our goal should be shown like this:

SQL>   with tt as
  2   (select t.ename,
  3           t.eid,
  4           (case
  5             when ename in
  6                  (SELECT t.ename /* t.*,LEVEL*/
  7                     FROM demp t
  8                    WHERE EXISTS
  9                    (SELECT 1 FROM demp t2 WHERE t2.eparent = t.eid)
 10                    START WITH eparent is null
 11                   CONNECT BY PRIOR eid = eparent) then
 12              null           -- If there's any son, then assume the value to null
 13             else
 14              eprogress      -- If there's no son
 15           end) eprogress,
 16           t.eparent,
 17           SYS_CONNECT_BY_PATH(EID, '/') || '/' path
 18      from DEMP t
 19     start with t.eparent is null
 20    connect by prior t.eid = t.eparent)
 21  SELECT eid,
 22         eparent,
 23         eprogress,
 24         ename,
 25         (SELECT avg(eprogress) FROM tt WHERE path like t1.path || '%') s_value
 26    FROM tt T1 order by eid;

       EID    EPARENT  EPROGRESS ENAME                                                           S_VALUE
---------- ---------- ---------- ------------------------------------------------------------ ----------
         1                       ename1                                                               50
         2          1         40 ename2                                                               40
         3          1         60 ename3                                                               60
         4          1            ename4                                                               50
         5          4         20 ename5                                                               20
         6          4         80 ename6                                                               80
        10                       ename10                                                      40.3333333  -- WRONG! his son is ename11 and ename12, it should be (10.5+100)/2=55.25
        11         10            ename11                                                            10.5
        12         10        100 ename12                                                             100
        13         11            ename13                                                            10.5
        14         13          1 ename14                                                               1
        15         13         20 ename15                                                              20



I do not know what is wrong. the value of ename10 should be the average of his sons(10.5 and 100) rather than grandsons.
What will you do to relize this kind of requests?

and Connor said...

We had a very similar scenario a while back that I did a video on - start with that, and see if it answers your question




Is this answer out of date? If it is, please let us know via a Comment