Re: Order in a hierarchical select

From: Pascal Glauser <glauser_at_my-deja.com>
Date: Sat, 18 Dec 1999 21:38:18 GMT
Message-ID: <83gusa$2m3$1_at_nnrp1.deja.com>


Craig

As far as I understand your select-statement, it works only for two-level-trees; for the third level you had to insert a third sort-field.
I personally am disappointed in the hierarchical query - too many restrictions (no joins, no use of views, no hierarchical order by). I either used some help-field or temporary data filled with procedural logic as suggested by the former reply, or a recursive function that delivers the concatenated name of the parents about like in your solution, but extended for arbitrary depths (or almost):

create or replace function emp_mgr_names (pEmpno in number)
return varchar2
-- return concatenated string of emp's mgr's names and his own name is
  Cursor curEmp (pEmpno in number) is
  select ename, mgr
  from scott.emp
  where empno = pEmpno ;

  recEmp curEmp%rowtype ;

  wResult Varchar2(2000) ;
  /* why not ? - runtime considerations see below */   /* max levels is lenght of wResult divided by lenght of names */

begin
  open curEmp(pEmpno) ;
  fetch curEmp into recEmp ;
  close curEmp ;

  if recEmp.mgr is not null then

  • we are not the boss. Put our
  • mgr's name(s) in front of our
  • own name wResult := emp_mgr_names(recEmp.mgr) ; end if ;
    • finally, add our own name (to an eventually null wResult) wResult := wResult || recEmp.ename ;

  return (wResult) ;
end ;

Now, you can use this string as order-by-field:

select empno, ename, emp_mgr_names(empno) sort_mgr from scott.emp
order by sort_mgr

gives:

7839	King      	King
7698	Blake     	King      Blake
7499	Allen     	King      Blake     Allen
7900	James     	King      Blake     James
7654	Martin    	King      Blake     Martin
7844	Turner    	King      Blake     Turner
7521	Ward      	King      Blake     Ward
7782	Clark     	King      Clark
7934	Miller    	King      Clark     Miller
7566	Jones     	King      Jones
7902	Ford      	King      Jones     Ford
7369	Smith     	King      Jones     Ford      Smith
7788	Scott     	King      Jones     Scott
7876	Adams     	King      Jones     Scott     Adams

(This example works only because the names in scott.emp are right-padded with blanks, which is rather unusual. With real-life-data, you had to right-padd them yourself in the function).

I am quite sure there is no non-procedural-solution, as you can't use the result of a function in a prior-clause (and thus can't force the recursive technique into the select-statement).

However, there is a drawback to this solution: the cursor in the function is opened 39 times for the query above (once for every delivered name), which is quite a lot. And it get's worse very fast, if the result-set get's bigger. This is because we visit every node in the tree and parse the tree towards it's root once for every node. The Mgr's of Jones for example are calculated many times, once for every of it's subordinates and once for their subordinates... A much better technique is to write a depth-first-algorithm starting at the root of the tree, but in this case you need to write some temporary data.

Regards
Pascal

In article <qfJ64.262$_f.762_at_dfiatx1-snr1.gtei.net>,   "Craig Ledbetter" <craigl_at_gte.net> wrote:
> Karoly,
> Here's another way. Your basic problem is that you want the
hierarcical
> output, but you want it ordered first by the parent record name, then
by the
> child record name. You just make a couple of sort fields in the
hierarchical
> query, then use it as an in line subquery in the FROM clause so that
you can
> ORDER BY the sort fields afterwards (can't use ORDER BY in view
accessed
> with CONNECT BY). Using "PRIOR Name" gets you the parent name, if
there is
> one. If there is no parent (ie, Child_id is null) then you have to be
> careful that you default a value into the second sort field so that
it will
> sort before any record that has a child. To do this I converted your
> character name to ascii, and defaulted to zero when there was no
parent.
> There are probably other ways to do this, but the concept is the same.
>
> Here is the spool file:
>
...
>
> SELECT
> RESULT
> FROM
> (SELECT
> DECODE(CHILD_ID,
> NULL, NAME,
> LPAD('|', (LEVEL -1)*2, ' ')||'-'||NAME
> ) RESULT,
> NVL(PRIOR NAME, NAME) FIRST_SORT,
> DECODE(CHILD_ID,
> NULL, 0,
> ASCII(NAME)
> ) SEC_SORT
> FROM
> TEST_TABLE
> CONNECT BY PRIOR ID = CHILD_ID
> START WITH CHILD_ID IS NULL
> )
> ORDER BY
> FIRST_SORT,
> SEC_SORT;
>
> RESULT
> ------------------------------
> c
> |-b
> |-e
> |-f
> d
> |-a
>
> Which is just what you wanted.
> Regards,
> Craig
>
> >>Problem description:
> >>
> >>I want to have an order by name within a hierarchical select:
> >>SELECT -1, Level, Name, null, to_char(ID)
> >> FROM TABLE
> >> CONNECT BY PRIOR ID=CHILD_ID
> >> START WITH ID IN
> >> (SELECT ID
> >> FROM TABLE
> >> WHERE CHILD_ID IS NULL);
> >>
> >>
> >>The original table content is:
> >>
> >>ID Name Child_ID
> >>-----------------------
> >>1 a 3
> >>2 b 4
> >>3 d null
> >>4 c null
> >>5 f 4
> >>6 e 4
> >>
> >>The hierarchical select result should look like:
> >>
> >>c
> >> |-b
> >> |-e
> >> |-f
> >>d
> >> |-a
> >>
>
>

Sent via Deja.com http://www.deja.com/
Before you buy. Received on Sat Dec 18 1999 - 22:38:18 CET

Original text of this message