13.05.2013 Views

Thanks for the question regarding "connect by ", versi

Thanks for the question regarding "connect by ", versi

Thanks for the question regarding "connect by ", versi

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

Home > Question Details<br />

Questions Resources Archives Links Popular Hot Files<br />

gaurang -- <strong>Thanks</strong> <strong>for</strong> <strong>the</strong> <strong>question</strong> <strong>regarding</strong> "<strong>connect</strong> <strong>by</strong> ", <strong>versi</strong>on 8.1.6<br />

Submitted on 11-Apr-2001 13:05 Central time zone Tom's latest followup | Bookmark | Bottom<br />

Last updated 30-Mar-2012 7:17<br />

You Asked<br />

hi tom<br />

can u explain me in detail about "start with <strong>connect</strong> <strong>by</strong>" sql statement (tree<br />

structure).i know <strong>the</strong>re is documentatin but it is very confusing.<br />

and we said...<br />

It builds a hierarchical query.<br />

There are 2 components to is:<br />

"start with" -- this identifies all LEVEL=1 nodes in <strong>the</strong> tree<br />

"<strong>connect</strong> <strong>by</strong>" -- describes how to walk from <strong>the</strong> parent nodes above to <strong>the</strong>ir children and<br />

<strong>the</strong>ir childrens children.<br />

Easiest to use an example on emp. If we start with "where mgr is NULL", we generate <strong>the</strong><br />

set of employees that have no mgr (<strong>the</strong>y are <strong>the</strong> top of <strong>the</strong> tree). If we<br />

CONNECT BY PRIOR EMPNO = /* current */ MGR<br />

that will take all of <strong>the</strong> PRIOR records (<strong>the</strong> start with at first) and find all records<br />

such that <strong>the</strong> MGR column equals <strong>the</strong>ir EMPNO (find all <strong>the</strong> records of people managed <strong>by</strong><br />

<strong>the</strong> people we started with).<br />

Using EMP, <strong>the</strong> start with SET is:<br />

scott@ORA8I.WORLD> select ename, empno, mgr from emp<br />

2 where mgr is null;<br />

ENAME EMPNO MGR<br />

---------- ---------- ----------<br />

KING 7839<br />

Now, if we do <strong>the</strong> "<strong>connect</strong> <strong>by</strong> manually" we would find:<br />

scott@ORA8I.WORLD> select ename, empno, mgr<br />

2 from emp where mgr = 7839;<br />

ENAME EMPNO MGR<br />

---------- ---------- ----------<br />

JONES 7566 7839<br />

BLAKE 7698 7839<br />

CLARK 7782 7839<br />

scott@ORA8I.WORLD><br />

KINGS empno is <strong>the</strong> prior empno. If we build <strong>the</strong> entire hierarch -- we have:<br />

scott@ORA8I.WORLD> select lpad(' ',level*2,' ')||ename ename, empno, mgr<br />

2 from emp<br />

3 START WITH MGR IS NULL<br />

4 CONNECT BY PRIOR EMPNO = MGR<br />

5 /<br />

ENAME EMPNO MGR<br />

--------------- ---------- ----------


KING 7839<br />

JONES 7566 7839<br />

SCOTT 7788 7566<br />

ADAMS 7876 7788<br />

FORD 7902 7566<br />

SMITH 7369 7902<br />

BLAKE 7698 7839<br />

ALLEN 7499 7698<br />

WARD 7521 7698<br />

MARTIN 7654 7698<br />

TURNER 7844 7698<br />

JAMES 7900 7698<br />

CLARK 7782 7839<br />

MILLER 7934 7782<br />

14 rows selected.<br />

So, KING is <strong>the</strong> start with set <strong>the</strong>n JONES BLAKE and CLARK fall under him. Each of <strong>the</strong>m<br />

becomes <strong>the</strong> PRIOR record in turn and <strong>the</strong>ir trees are expanded.<br />

Reviews<br />

April 12, 2001 - 8am Central time zone Bookmark | Bottom | Top<br />

Reviewer: gaurang<br />

useful summary of <strong>connect</strong> <strong>by</strong> September 25, 2002 - 2pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: RParr from Seattle, WA U.S.A.<br />

in a fraction of <strong>the</strong> space you provided a much better overview of <strong>connect</strong> <strong>by</strong> and basic heirarchical<br />

query.<br />

Nice overview January 22, 2003 - 1am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Anirudh from New delhi, India<br />

Hi Tom<br />

Your explaination about <strong>the</strong> <strong>connect</strong> <strong>by</strong> clause was very helpfull. However, I have a <strong>question</strong><br />

we have got a function<br />

FUNCTION Get_Parent_Entity_Id<br />

(<br />

in_test_pgm_id number,<br />

in_test_admin_id number,<br />

ic_child_entity_type_code varchar2,<br />

in_child_entity_id number,<br />

ic_parent_entity_type_code varchar2<br />

)<br />

RETURN number IS<br />

ln_count NUMBER;<br />

BEGIN<br />

ln_count := 0;<br />

BEGIN<br />

select n_parent_entity_id<br />

into ln_count<br />

from<br />

(<br />

select<br />

n_test_pgm_id,<br />

n_test_admin_id,<br />

c_parent_entity_type_code,<br />

n_parent_entity_id<br />

from<br />

rpt_entity_struc res<br />

start with<br />

res.n_test_pgm_id = in_test_pgm_id and<br />

res.n_test_admin_id = in_test_admin_id and<br />

res.n_entity_struc_id = 0 and<br />

res.c_child_entity_type_code = ic_child_entity_type_code and<br />

res.n_child_entity_id = in_child_entity_id<br />

<strong>connect</strong> <strong>by</strong><br />

res.n_test_pgm_id = prior n_test_pgm_id and<br />

res.n_test_admin_id = prior n_test_admin_id and<br />

res.n_entity_struc_id = prior n_entity_struc_id and


es.c_child_entity_type_code = prior c_parent_entity_type_code and<br />

res.n_child_entity_id = prior n_parent_entity_id<br />

)<br />

where c_parent_entity_type_code = ic_parent_entity_type_code;<br />

EXCEPTION<br />

WHEN NO_DATA_FOUND THEN<br />

ln_count := 0;<br />

WHEN OTHERS THEN<br />

ln_err_num := SQLCODE;<br />

lc_err_msg := 'Error in Get_Parent_Entity_Id - ' || substr(sqlerrm, 1, 200);<br />

raise_application_error(-20000, lc_err_msg);<br />

END;<br />

RETURN ln_count;<br />

--<br />

Algo:n_parent_entity_id is retrieved based on <strong>the</strong> input parameters passed besides <strong>the</strong><br />

ic_parent_entity_type_code which is one of <strong>the</strong> important input columns to be considered. The<br />

Hierarchical query (STARTWITH and CONNECT BY) clauses are used to find <strong>the</strong> root parent id.<br />

Question<br />

The <strong>connect</strong> <strong>by</strong> prior clause here refers to <strong>the</strong> columns and one of those (n_entity_struc_id ) is not<br />

in <strong>the</strong> select list. My <strong>question</strong> is what is <strong>the</strong> use of that PRIORing <strong>the</strong> column when it has not been<br />

selected.?<br />

Followup January 22, 2003 - 8am Central time zone:<br />

<strong>the</strong> same as when you use it in a predicate, no different.<br />

select ename from emp where empno = :x<br />

what is <strong>the</strong> use of empno in <strong>the</strong> predicate if it has not been selected? well, it is used to<br />

identify what record(s) you want. same with columns in <strong>the</strong> start with, <strong>connect</strong> <strong>by</strong> prior and so<br />

on...<br />

Urgent Requirement May 22, 2003 - 10am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Shrikant Gavas from India<br />

Please go throught <strong>the</strong> script :<br />

CREATE TABLE MY_LEVEL1 (<br />

ORIG_RECP VARCHAR2 (10),<br />

ORIG_AMT NUMBER,<br />

REF_RECP VARCHAR2 (10),<br />

REF_AMT NUMBER,<br />

PARTY VARCHAR2 (6) ) ;<br />

INSERT INTO MY_LEVEL1 ( ORIG_RECP, ORIG_AMT, REF_RECP, REF_AMT,<br />

PARTY ) VALUES (<br />

'100', 10000, NULL, 500, 'A0001');<br />

INSERT INTO MY_LEVEL1 ( ORIG_RECP, ORIG_AMT, REF_RECP, REF_AMT,<br />

PARTY ) VALUES (<br />

'110', 300, '100', 100, 'A0001');<br />

INSERT INTO MY_LEVEL1 ( ORIG_RECP, ORIG_AMT, REF_RECP, REF_AMT,<br />

PARTY ) VALUES (<br />

'120', 200, '110', 50, 'A0001');<br />

INSERT INTO MY_LEVEL1 ( ORIG_RECP, ORIG_AMT, REF_RECP, REF_AMT,<br />

PARTY ) VALUES (<br />

'130', 100, '120', 30, 'A0001');<br />

INSERT INTO MY_LEVEL1 ( ORIG_RECP, ORIG_AMT, REF_RECP, REF_AMT,<br />

PARTY ) VALUES (<br />

'300', 10000, NULL, 500, 'A0001');<br />

INSERT INTO MY_LEVEL1 ( ORIG_RECP, ORIG_AMT, REF_RECP, REF_AMT,<br />

PARTY ) VALUES (<br />

'310', 300, '300', 100, 'A0001');<br />

INSERT INTO MY_LEVEL1 ( ORIG_RECP, ORIG_AMT, REF_RECP, REF_AMT,<br />

PARTY ) VALUES (<br />

'320', 200, '310', 50, 'A0001');<br />

INSERT INTO MY_LEVEL1 ( ORIG_RECP, ORIG_AMT, REF_RECP, REF_AMT,<br />

PARTY ) VALUES (<br />

'330', 100, '320', 30, 'A0001');<br />

INSERT INTO MY_LEVEL1 ( ORIG_RECP, ORIG_AMT, REF_RECP, REF_AMT,


PARTY ) VALUES (<br />

'100', 10000, NULL, 500, 'A0003');<br />

INSERT INTO MY_LEVEL1 ( ORIG_RECP, ORIG_AMT, REF_RECP, REF_AMT,<br />

PARTY ) VALUES (<br />

'110', 300, '100', 100, 'A0003');<br />

INSERT INTO MY_LEVEL1 ( ORIG_RECP, ORIG_AMT, REF_RECP, REF_AMT,<br />

PARTY ) VALUES (<br />

'120', 200, '110', 50, 'A0003');<br />

INSERT INTO MY_LEVEL1 ( ORIG_RECP, ORIG_AMT, REF_RECP, REF_AMT,<br />

PARTY ) VALUES (<br />

'130', 100, '120', 30, 'A0003');<br />

INSERT INTO MY_LEVEL1 ( ORIG_RECP, ORIG_AMT, REF_RECP, REF_AMT,<br />

PARTY ) VALUES (<br />

'100', 10000, NULL, 500, 'A0004');<br />

INSERT INTO MY_LEVEL1 ( ORIG_RECP, ORIG_AMT, REF_RECP, REF_AMT,<br />

PARTY ) VALUES (<br />

'110', 300, '100', 100, 'A0004');<br />

INSERT INTO MY_LEVEL1 ( ORIG_RECP, ORIG_AMT, REF_RECP, REF_AMT,<br />

PARTY ) VALUES (<br />

'120', 200, '110', 50, 'A0004');<br />

INSERT INTO MY_LEVEL1 ( ORIG_RECP, ORIG_AMT, REF_RECP, REF_AMT,<br />

PARTY ) VALUES (<br />

'130', 100, '120', 30, 'A0004');<br />

INSERT INTO MY_LEVEL1 ( ORIG_RECP, ORIG_AMT, REF_RECP, REF_AMT,<br />

PARTY ) VALUES (<br />

'130', 100, '120', 30, 'A0004');<br />

select level x,<br />

party, orig_recp, ref_recp, orig_amt, ref_amt<br />

from my_level1<br />

where party between 'A0001' AND 'A0003'<br />

<strong>connect</strong> <strong>by</strong><br />

prior orig_recp = ref_recp and<br />

prior party = party<br />

start with ref_recp is null<br />

1 A0001 100 10000 500<br />

2 A0001 110 100 300 100<br />

3 A0001 120 110 200 50<br />

4 A0001 130 120 100 30<br />

1 A0001 300 10000 500<br />

2 A0001 310 300 300 100<br />

3 A0001 320 310 200 50<br />

4 A0001 330 320 100 30<br />

1 A0003 100 10000 500<br />

2 A0003 110 100 300 100<br />

3 A0003 120 110 200 50<br />

4 A0003 130 120 100 30<br />

Clients desired output :<br />

1 A0001 100 10000 500<br />

4 A0001 130 120 100 30<br />

1 A0001 300 10000 500<br />

4 A0001 330 320 100 30<br />

1 A0003 100 10000 500<br />

4 A0003 130 120 100 30<br />

i.e. clients requirement is that <strong>the</strong>y want first and last row per level. We<br />

have tried a lot but not got any appropriate solution <strong>for</strong> this.<br />

Please provide some solution <strong>for</strong> above query asap.<br />

Interpret July 13, 2003 - 2pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Nitin from Atlanta<br />

How do I interpret this SQL and <strong>the</strong> output? Is <strong>the</strong> output correct?<br />

SELECT<br />

TO_CHAR(A.EFFDT,'YYYY-MM-DD'), A.TREE_NODE_NUM, A.TREE_NODE,<br />

A.TREE_NODE_NUM_END, A.TREE_LEVEL_NUM, A.TREE_NODE_TYPE, A.PARENT_NODE_NUM, A.OLD_TREE_NODE_NUM<br />

From PSTREENODE A<br />

Where A.TREE_NAME = 'OVER_EXP'<br />

And A.SETID = 'LOCKE'


And A.SETID = 'LOCKE'<br />

And A.EFFDT = (Select Max(Z.EFFDT)<br />

From PSTREENODE Z<br />

Where Z.TREE_NAME = A.TREE_NAME<br />

And Z.TREE_NODE = A.TREE_NODE<br />

And Z.SETID = A.SETID)<br />

Start With TREE_NODE = 'WORK'<br />

Connect By PARENT_NODE_NUM = Prior TREE_NODE_NUM<br />

TO_CHAR(A. TREE_NODE_NUM TREE_NODE TREE_NODE_NUM_END TREE_LEVEL_NUM T PARENT_NODE_NUM O<br />

---------- ------------- -------------------- ----------------- -------------- - --------------- -<br />

2002-01-01 222222223 WORK 333333333 0 G 1 N<br />

2002-01-01 1222222222 CASE 1333333332 0 G 1111111111 N<br />

2002-01-01 1166666666 DEPT 1222222221 0 G 1111111111 N<br />

2002-01-01 1222222222 CASE 1333333332 0 G 1111111111 N<br />

2002-01-01 1166666666 DEPT 1222222221 0 G 1111111111 N<br />

Followup July 14, 2003 - 12am Central time zone:<br />

<strong>the</strong> output is correct -- given <strong>the</strong> query.<br />

However, without any clue as to <strong>the</strong> "<strong>question</strong>" - no one can really tell you if <strong>the</strong> output correctly<br />

answers your <strong>question</strong>!<br />

Clarification July 13, 2003 - 11pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Nitin from Atlanta<br />

The SQL provided in my previous feedback finally made sense. I have <strong>the</strong> following <strong>question</strong>.<br />

The output is not what is desired. We need <strong>the</strong> condition<br />

Where A.TREE_NAME = 'OVER_EXP'<br />

And A.SETID = 'LOCKE'<br />

And A.EFFDT = (Select Max(Z.EFFDT)<br />

From PSTREENODE Z<br />

Where Z.TREE_NAME = A.TREE_NAME<br />

And Z.TREE_NODE = A.TREE_NODE<br />

And Z.SETID = A.SETID)<br />

to be applied prior to executing <strong>the</strong> CONNECT BY - PRIOR part of <strong>the</strong> SQL.<br />

Now, we observe that <strong>the</strong> SQL is first per<strong>for</strong>ming <strong>the</strong> CONNECT BY - PRIOR and <strong>the</strong>n <strong>the</strong> WHERE portion.<br />

Please let us know how it can be achieved.<br />

<strong>Thanks</strong><br />

Followup July 14, 2003 - 12am Central time zone:<br />

<strong>the</strong>n that condition should be in <strong>the</strong> <strong>connect</strong> <strong>by</strong> itself as well. you don't have to only do prior's<br />

in <strong>the</strong>re.<br />

start with /<strong>connect</strong> <strong>by</strong> is done<br />

AND THEN<br />

<strong>the</strong> where clause is applied<br />

If you want to stop building <strong>the</strong> tree when you hit some condition -- put that condition in <strong>the</strong><br />

<strong>connect</strong> <strong>by</strong><br />

chained <strong>connect</strong> <strong>by</strong> July 26, 2003 - 3am Central time zone Bookmark | Bottom | Top<br />

Reviewer: farweeda from qatar<br />

ihave docs tab containing two fields (doc_id , doc_rel_id)<br />

with e.g. following values {(5,2)(9,5)(11,2)(3,1)(4,2)(8,3)(6,4)} <strong>the</strong> doc_rel_id has some relation<br />

with doc_id and vise versa so when i'm asking <strong>for</strong> <strong>the</strong> related docs to specific doc_id e.g. doc_id


with doc_id and vise versa so when i'm asking <strong>for</strong> <strong>the</strong> related docs to specific doc_id e.g. doc_id<br />

=2 <strong>the</strong> resault should be : ( 5,11,4,9 because of 5 ,6 because of 4 ) and if i change doc_id to any<br />

of <strong>the</strong> resault's value suppose 4 <strong>the</strong> same resault should be <strong>the</strong>re<br />

Followup July 26, 2003 - 12pm Central time zone:<br />

ops$tkyte@ORA920LAP> select *<br />

2 from t<br />

3 start with doc_rel_id = 2<br />

4 <strong>connect</strong> <strong>by</strong> prior doc_id = doc_rel_id;<br />

DOC_ID DOC_REL_ID<br />

---------- ----------<br />

5 2<br />

9 5<br />

11 2<br />

4 2<br />

6 4<br />

To Srikant <strong>for</strong> his urgent query July 27, 2003 - 1pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: A reader<br />

I tried out your scenario - I got a query to get <strong>the</strong><br />

same set of records that you wanted but not in <strong>the</strong> same<br />

order - you can probably tweak it.<br />

Tom may have an even better solution of course!<br />

Menon:)<br />

Here goes:<br />

select x, party, orig_recp, ref_recp, orig_amt, ref_amt<br />

from<br />

(<br />

select a.*, first_value( x) over (partition <strong>by</strong> party order <strong>by</strong> party) first_x,<br />

last_value( x) over (partition <strong>by</strong> party order <strong>by</strong> party) last_x<br />

from<br />

(<br />

select level x, party, orig_recp, ref_recp, orig_amt, ref_amt<br />

from my_level1<br />

where party between 'A0001' AND 'A0003'<br />

<strong>connect</strong> <strong>by</strong><br />

prior orig_recp = ref_recp and<br />

prior party = party<br />

start with ref_recp is null<br />

) a<br />

) b<br />

where x = first_x or<br />

x = last_x;<br />

Skipping Gaps? November 23, 2003 - 12pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Doug from CT, USA<br />

Tom - is it possible to make a <strong>connect</strong> <strong>by</strong> skip gaps in <strong>the</strong> sequence? Like<br />

SQL> desc a;<br />

Name Null? Type<br />

----------------------------------------- -------- ----------------------<br />

ID NUMBER<br />

PRINUMBER NUMBER<br />

SQL> select * from a;<br />

ID PRINUMBER<br />

---------- ----------<br />

1 2<br />

2 3<br />

3 4<br />

9 10


9 10<br />

SQL> select id, prinumber from<br />

2 a start with id=1<br />

3 <strong>connect</strong> <strong>by</strong> prior prinumber=id;<br />

ID PRINUMBER<br />

---------- ----------<br />

1 2<br />

2 3<br />

3 4<br />

Is <strong>the</strong>re any way to move on to NINE and continue?<br />

Followup November 23, 2003 - 2pm Central time zone:<br />

what would be <strong>the</strong> possible use of this?<br />

how would this be any different from "select * from t"<br />

fair point November 23, 2003 - 3pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Doug from CT, USA<br />

Fair enough - maybe I am asking <strong>the</strong> wrong <strong>question</strong>.<br />

What I'm trying to do is take a time history, potentially with workspace manager that might look<br />

like this:<br />

SQL> select name, salary, starttime, stoptime, salary from doug;<br />

NAME SALARY STARTTIME STOPTIME SALARY<br />

---------- ---------- --------- --------- ----------<br />

Bob 6000 01-JAN-95 01-JUN-95 6000<br />

Bob 7000 01-JUN-95 01-OCT-95 7000<br />

Bob 7000 01-OCT-95 01-FEB-96 7000<br />

Bob 7000 01-FEB-96 01-JAN-97 7000<br />

Bob 6000 01-JAN-97 01-MAR-98 6000<br />

Bob 5000 01-APR-03 01-JUN-03 5000<br />

The startimes and stoptimes <strong>connect</strong> very nicely except <strong>the</strong>re is a gap between March 1998 and April<br />

2003.<br />

If I want to order <strong>the</strong>se dates and include a special salary "gap". We "don't know".. what Bob was<br />

doing between March,98 and April 2003.<br />

This is what happens at <strong>the</strong> root of <strong>the</strong> query I am looking at -<br />

SQL> l<br />

1 select name, salary, starttime, stoptime,decode( lag(salary) over (order <strong>by</strong><br />

starttime), salary,<br />

2* to_number(null), row_number() over (order <strong>by</strong> starttime) ) rn from doug<br />

SQL> /<br />

NAME SALARY STARTTIME STOPTIME RN<br />

---------- ---------- --------- --------- ----------<br />

Bob 6000 01-JAN-95 01-JUN-95 1<br />

Bob 7000 01-JUN-95 01-OCT-95 2<br />

Bob 7000 01-OCT-95 01-FEB-96<br />

Bob 7000 01-FEB-96 01-JAN-97<br />

Bob 6000 01-JAN-97 01-MAR-98 5<br />

Bob 5000 01-APR-03 01-JUN-03 6<br />

6 rows selected.<br />

This is WHAT I WANT more or less - <strong>the</strong> null values when salarys are <strong>the</strong> same so <strong>the</strong>y can be<br />

coalesed. What I REALLY want is THIS:<br />

NAME SALARY STARTTIME STOPTIME RN<br />

---------- ---------- --------- --------- ----------<br />

Bob 6000 01-JAN-95 01-JUN-95 1<br />

Bob 7000 01-JUN-95 01-OCT-95 2<br />

Bob 7000 01-OCT-95 01-FEB-96<br />

Bob 7000 01-FEB-96 01-JAN-97<br />

Bob 6000 01-JAN-97 01-MAR-98 5


Bob 6000 01-JAN-97 01-MAR-98 5<br />

Bob NULL 01-MAR-98 01-APR-03 6<br />

Bob 5000 01-APR-03 01-JUN-03 7<br />

Now if I just order <strong>by</strong> time with a "select * from T".. how could I "fill in" <strong>the</strong> gaps in <strong>the</strong><br />

sequence?<br />

<strong>Thanks</strong>,<br />

D.<br />

Followup November 23, 2003 - 5pm Central time zone:<br />

it was <strong>the</strong> connnect <strong>by</strong> that really confused me -- couldn't understand where that is coming in.<br />

Now that I know <strong>the</strong> <strong>question</strong>, an answer can be <strong>for</strong>thcoming :)<br />

Here is one technique:<br />

ops$tkyte@ORA920> select name,<br />

2 decode( r, 1, null, '' ) msg,<br />

3 decode( r, 1, salary, null ) salary,<br />

4 decode( r, 1, starttime, last_stop ) starttime,<br />

5 decode( r, 1, stoptime, starttime ) stoptime<br />

6 from (<br />

7 select name, salary,<br />

8 starttime, stoptime,<br />

9 lag(stoptime) over (partition <strong>by</strong> name order <strong>by</strong> starttime) last_stop,<br />

10 decode( lag(stoptime) over (partition <strong>by</strong> name order <strong>by</strong> starttime), starttime, null,<br />

null, null, 1 ) dup_me<br />

11 from t<br />

12 ),<br />

13 (select 1 r from dual union all select 2 r from dual)<br />

14 where r = 1 or dup_me = 1<br />

15 order <strong>by</strong> 4<br />

16 /<br />

NAME MSG SALARY STARTTIME STOPTIME<br />

------------------------------ --------- ---------- --------- ---------<br />

Bob 6000 01-JAN-95 01-JUN-95<br />

Bob 7000 01-JUN-95 01-OCT-95<br />

Bob 7000 01-OCT-95 01-FEB-96<br />

Bob 7000 01-FEB-96 01-JAN-97<br />

Bob 6000 01-JAN-97 01-MAR-98<br />

Bob 01-MAR-98 01-APR-03<br />

Bob 5000 01-APR-03 01-JUN-03<br />

7 rows selected.<br />

Stunning November 23, 2003 - 7pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Doug from CT, USA<br />

Tom - that is very crafty. I learn a lot about SQL from you. Is <strong>the</strong>re a bit of inefficiency built<br />

into that however? The join with <strong>the</strong> 2 rows in <strong>the</strong> dual table, doesn't that increase <strong>the</strong> work <strong>for</strong><br />

<strong>the</strong> db? Don't get me wrong I'm not complaining. :-) Very nice solution. I wasn't sure it could<br />

be done. Analytic functions seem to have all sorts of interesting uses.<br />

Followup November 24, 2003 - 7am Central time zone:<br />

well -- in order to "make up" data we need to join -- to syn<strong>the</strong>size that row, it was somewhat<br />

unavoidable (joining to a two row table). pipelined functions and procedural code might have<br />

worked as well, but this is easier.<br />

<strong>connect</strong> <strong>by</strong> but parent in different table December 2, 2003 - 2am Central time zone<br />

Bookmark | Bottom | Top<br />

Reviewer: umesh from blore india<br />

Tom


Tom<br />

I have a situation<br />

There are 2 tables srch_criteria, srch_hierarchy<br />

srch_criteria is <strong>the</strong> master<br />

-----------------------------------------------------id<br />

name<br />

07 Aerospace Tier 1<br />

08 Functional Tier 2<br />

09 Finance Tier 1<br />

10 x Tier 1<br />

11 y Tier 1<br />

12 z Tier 3<br />

.....<br />

srch_hierarchy has <strong>the</strong> hierarchy maintained in it<br />

sc_id_child sc_id_parent<br />

-------------------------------------<br />

08 07<br />

09 07<br />

081 08<br />

082 08<br />

091 09<br />

092 09<br />

search hierarchy will have a hierachy<br />

07 is linked with 08,09<br />

08 has children in 081 , 082<br />

09 has children in 091, 092<br />

( I agree bad design)<br />

I take only Tier 1 from <strong>the</strong> top and traverse thru whole of children and grand children in <strong>the</strong><br />

hierarchy table<br />

I need a query <strong>for</strong> it Is it possible in SQL or should i write procedure or function<br />

Followup December 2, 2003 - 8am Central time zone:<br />

I don't see "a bad design" here? looks pretty typical.<br />

I do not know what "tier 1" from <strong>the</strong> "top" is though. hows about you show us <strong>the</strong> desired output<br />

and explain how it was arrived at.<br />

using an order <strong>by</strong> December 2, 2003 - 11am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Doug from CT, USA<br />

Tom - as you pointed out to me earlier, in some situations gaps or duplicates with a <strong>connect</strong> <strong>by</strong> are<br />

redundant because <strong>the</strong>y are <strong>the</strong> same as an order <strong>by</strong> clause.<br />

In a situation where I want to ensure <strong>the</strong> order but not select <strong>the</strong> column I am ordering <strong>by</strong> - will<br />

this work?<br />

select T.x, T.y from (select x,y,z from R order <strong>by</strong> z ) T<br />

It <strong>the</strong> order ensured?<br />

Followup December 2, 2003 - 12pm Central time zone:<br />

just<br />

select x, y<br />

from R<br />

order <strong>by</strong> Z;


December 9, 2003 - 2am Central time zone Bookmark | Bottom | Top<br />

Reviewer: umesh from blore<br />

SQL> l<br />

1 SELECT<br />

2 DECODE ( ll ,1 , crit1.name ) one ,<br />

3 DECODE ( ll ,2 , crit1.name) two ,<br />

4 DECODE ( ll ,3 , crit1.name) three ,<br />

5 DECODE ( ll ,4 , crit1.name) four<br />

6 FROM<br />

7 (SELECT sc_id_child , sc_id_parent , LEVEL ll FROM CNT_SEARCH_HIERARCHY src<br />

8 START WITH sc_id_parent='crite000000000000002'<br />

9 CONNECT BY PRIOR sc_id_child=sc_id_parent<br />

10 ) , CNT_SEARCH_CRITERIA crit , CNT_SEARCH_CRITERIA crit1<br />

11 WHERE crit.id = sc_id_parent<br />

12* AND crit1.id=sc_id_child<br />

SQL> /<br />

ONE TWO THREE FOUR<br />

-------------------------- --------------------------- ------------------------<br />

-------------------<br />

Building Automation<br />

ACSELON<br />

Authorized Trainer<br />

Programs<br />

Delivery & Installation<br />

ADEPT<br />

Project<br />

Management<br />

Human Resources<br />

Love it, but ... March 4, 2004 - 3pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Peter Tran from Houston, TX USA<br />

Hi Tom,<br />

What happens if you have a dataset where <strong>the</strong> top mgr refers to himself? Using your example, if <strong>the</strong><br />

MGR = 7839 <strong>for</strong> KING and I run <strong>the</strong> same query you have Oracle gives me a:<br />

ORA-01436: CONNECT BY loop in user data<br />

A <strong>connect</strong> <strong>by</strong> record cannot reference itself o<strong>the</strong>rwise we have an infinite loop situation.<br />

Is <strong>the</strong>re anyway I can filter this out? In o<strong>the</strong>r words, I still want <strong>the</strong> answer you got earlier,<br />

but now I have <strong>the</strong> situation where KING refers to himself in <strong>the</strong> MGR column.<br />

<strong>Thanks</strong>,<br />

-Peter<br />

Followup March 4, 2004 - 3pm Central time zone:<br />

that case is easy -- since we have <strong>the</strong> "loop" in a single row, we can simply filter it out in <strong>the</strong><br />

"<strong>connect</strong> <strong>by</strong>" clause:<br />

ops$tkyte@ORA9IR2> create table emp as select * from scott.emp;<br />

Table created.<br />

ops$tkyte@ORA9IR2> update emp set mgr = empno where mgr is null;<br />

1 row updated.<br />

ops$tkyte@ORA9IR2> commit;<br />

Commit complete.


ops$tkyte@ORA9IR2><br />

ops$tkyte@ORA9IR2> select lpad(' ',level*2,' ')||ename ename, empno, mgr<br />

2 from emp<br />

3 START WITH ename = 'KING'<br />

4 CONNECT BY PRIOR EMPNO = MGR<br />

5 /<br />

ERROR:<br />

ORA-01436: CONNECT BY loop in user data<br />

no rows selected<br />

ops$tkyte@ORA9IR2> select lpad(' ',level*2,' ')||ename ename, empno, mgr<br />

2 from emp<br />

3 START WITH ename = 'KING'<br />

4 CONNECT BY PRIOR EMPNO = MGR AND empno mgr<br />

5 /<br />

ENAME EMPNO MGR<br />

--------------- ---------- ----------<br />

KING 7839 7839<br />

JONES 7566 7839<br />

SCOTT 7788 7566<br />

ADAMS 7876 7788<br />

FORD 7902 7566<br />

SMITH 7369 7902<br />

BLAKE 7698 7839<br />

ALLEN 7499 7698<br />

WARD 7521 7698<br />

MARTIN 7654 7698<br />

TURNER 7844 7698<br />

JAMES 7900 7698<br />

CLARK 7782 7839<br />

MILLER 7934 7782<br />

14 rows selected.<br />

And in 10g, you have NOCYCLE to avoid <strong>the</strong> loops anywhere:<br />

ops$tkyte@ORA10G> select lpad(' ',level*2,' ')||ename ename, empno, mgr<br />

2 from emp<br />

3 START WITH ename = 'KING'<br />

4 CONNECT BY NOCYCLE PRIOR EMPNO = MGR<br />

5 /<br />

ENAME EMPNO MGR<br />

--------------- ---------- ----------<br />

KING 7839 7839<br />

JONES 7566 7839<br />

FORD 7902 7566<br />

SMITH 7369 7902<br />

SCOTT 7788 7566<br />

ADAMS 7876 7788<br />

BLAKE 7698 7839<br />

ALLEN 7499 7698<br />

WARD 7521 7698<br />

MARTIN 7654 7698<br />

TURNER 7844 7698<br />

JAMES 7900 7698<br />

CLARK 7782 7839<br />

MILLER 7934 7782<br />

14 rows selected.<br />

Awesome... March 4, 2004 - 4pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Peter Tran from Houston, TX USA<br />

<strong>Thanks</strong> <strong>for</strong> <strong>the</strong> quick turn-around.<br />

The combination of Tom Kyte and Oracle really rocks.<br />

Awesome...awesome...awesome.


-Peter<br />

Can I get <strong>the</strong> parent position w.r.t. to <strong>the</strong> rownum? March 5, 2004 - 4pm Central time zone<br />

Bookmark | Bottom | Top<br />

Reviewer: Peter Tran from Houston, TX USA<br />

Hi Tom,<br />

Is it possible <strong>for</strong> me to generate <strong>the</strong> ParentPosition index w.r.t. to <strong>the</strong> assigned ROWNUM? Of<br />

course, ROWNUM starts at 1, but my example below is using base 0. Ei<strong>the</strong>r way, you can see where<br />

I'm getting at with <strong>the</strong> example below.<br />

Currently, I'm doing this mapping in code. It would be much better if I can do this within SQL.<br />

<strong>Thanks</strong>,<br />

-Peter<br />

ROWNUM ENAME EMPNO MGR ParentPos<br />

------ --------------- ---------- ---------- -------------<br />

0 KING 7839 7839 0<br />

1 JONES 7566 7839 0<br />

2 FORD 7902 7566 1<br />

3 SMITH 7369 7902 2<br />

4 SCOTT 7788 7566 1<br />

5 ADAMS 7876 7788 4<br />

6 BLAKE 7698 7839 0<br />

7 ALLEN 7499 7698 6<br />

8 WARD 7521 7698 6<br />

9 MARTIN 7654 7698 6<br />

10 TURNER 7844 7698 6<br />

11 JAMES 7900 7698 6<br />

12 CLARK 7782 7839 0<br />

13 MILLER 7934 7782 12<br />

Followup March 5, 2004 - 7pm Central time zone:<br />

what is "parentPos"<br />

ParentPos March 6, 2004 - 5pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Peter Tran from Houston, TX USA<br />

ParentPos is <strong>the</strong> RowNum value of <strong>the</strong> parent.<br />

King is <strong>the</strong> parent of Jones, Blake, and Clark which is why ParentPos <strong>for</strong> <strong>the</strong>m is 0.<br />

Blake is <strong>the</strong> parent of Alan, Ward, Martin, Turner, and James, so you see <strong>the</strong>ir ParentPos is 6<br />

because Blake's RowNum is 6.<br />

<strong>Thanks</strong>,<br />

-Peter<br />

Followup March 6, 2004 - 8pm Central time zone:<br />

scott@ORA9IR2> select rnum, ename, empno, mgr,<br />

2 substr( scbp2, 1, instr(scbp2,',')-1 ) parentpos<br />

3 from (<br />

4 select a.*,<br />

5 substr( scbp, instr(scbp, ',', -1, 2 )+1 ) scbp2<br />

6 from (<br />

7 select rownum-1 rnum, rpad( ' ', 2*level, ' ' ) || ename ename, empno, mgr,<br />

8 sys_<strong>connect</strong>_<strong>by</strong>_path( rownum-1, ',' ) scbp<br />

9 from emp<br />

10 start with mgr is null<br />

11 <strong>connect</strong> <strong>by</strong> prior empno = mgr<br />

12 ) a


12 ) a<br />

13 )<br />

14 /<br />

RNUM ENAME EMPNO MGR PARENT<br />

---------- -------------------- ---------- ---------- ------<br />

0 KING 7839<br />

1 JONES 7566 7839 0<br />

2 SCOTT 7788 7566 1<br />

3 ADAMS 7876 7788 2<br />

4 FORD 7902 7566 1<br />

5 SMITH 7369 7902 4<br />

6 BLAKE 7698 7839 0<br />

7 ALLEN 7499 7698 6<br />

8 WARD 7521 7698 6<br />

9 MARTIN 7654 7698 6<br />

10 TURNER 7844 7698 6<br />

11 JAMES 7900 7698 6<br />

12 CLARK 7782 7839 0<br />

13 MILLER 7934 7782 12<br />

14 rows selected.<br />

Just amazing... March 6, 2004 - 9pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Peter Tran from Houston, TX USA<br />

Excellent!<br />

<strong>Thanks</strong>!<br />

-Peter<br />

CONNECT BY March 9, 2004 - 12am Central time zone Bookmark | Bottom | Top<br />

Reviewer: ANOOP GUPTA from INDIA<br />

it would very useful <strong>for</strong> me to understand <strong>connect</strong> <strong>by</strong><br />

specially to get first and last in tree.<br />

one solution i also have <strong>for</strong> geting first and last<br />

in tree.<br />

select *<br />

from<br />

(<br />

select level x, party, orig_recp, ref_recp, orig_amt, ref_amt<br />

from my_level1<br />

where party between 'A0001' AND 'A0003'<br />

<strong>connect</strong> <strong>by</strong><br />

prior orig_recp = ref_recp and<br />

prior party = party<br />

start with ref_recp is null<br />

) a<br />

where a.ref_recp is null<br />

or a.orig_recp not in (select nvl(ref_recp,'NULL')<br />

from my_level1<br />

where party between 'A0001' AND 'A0003'<br />

<strong>connect</strong> <strong>by</strong><br />

prior orig_recp = ref_recp and<br />

prior party = party<br />

start with ref_recp is null<br />

)<br />

its output is similar as your client want<br />

1 A0001 100 10000 500<br />

4 A0001 130 120 100 30<br />

1 A0001 300 10000 500<br />

4 A0001 330 320 100 30<br />

1 A0003 100 10000 500<br />

4 A0003 130 120 100 30


Find Employee's top most manager August 19, 2004 - 6pm Central time zone<br />

Bookmark | Bottom | Top<br />

Reviewer: Mita from NJ<br />

For <strong>the</strong> following data,<br />

Emp_id Mgr_id<br />

1 2<br />

2 3<br />

3 4<br />

4<br />

5 6<br />

6 7<br />

7<br />

I need <strong>the</strong> following result<br />

EmpId TopManager<br />

1 4<br />

2 4<br />

3 4<br />

4 4<br />

5 7<br />

6 7<br />

7 7<br />

How can I achieve this ??<br />

Followup August 19, 2004 - 8pm Central time zone:<br />

if you gave me a create table and insert into's, i'd show you how to use a scalar subquery with<br />

<strong>connect</strong> <strong>by</strong> using max to do this.<br />

maybe that is enough to get you going....<br />

Sample Data August 24, 2004 - 12pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Mita from NJ<br />

create table emp (emp_Id number, mgr_Id number);<br />

insert into emp values(1,2);<br />

insert into emp values(2,3);<br />

insert into emp values(3,4);<br />

insert into emp values(4,Null);<br />

insert into emp values(5,6);<br />

insert into emp values(6,7);<br />

insert into emp values(7,Null);<br />

Followup August 24, 2004 - 3pm Central time zone:<br />

ops$tkyte@ORA9IR2> select emp_id,<br />

2 to_number( substr(<br />

3 (select max( to_char(level,'fm000009') || ' ' || emp_id )<br />

4 from emp e2<br />

5 start with e2.emp_id = e1.emp_id<br />

6 <strong>connect</strong> <strong>by</strong> prior mgr_id is not null and prior mgr_id = emp_id )<br />

7 , 8 ) ) top_mgr<br />

8 from emp e1<br />

9 /<br />

EMP_ID TOP_MGR<br />

---------- ----------<br />

1 4<br />

2 4


2 4<br />

3 4<br />

4 4<br />

5 7<br />

6 7<br />

7 7<br />

7 rows selected.<br />

More Connect <strong>by</strong> October 14, 2004 - 5pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Vinnie from Orlando<br />

Tom,<br />

I have <strong>the</strong> following:<br />

create table parent( rowid number(2) primary key, id varchar2(14) );<br />

create table child( rowid_parent number(2), id varchar2(14) );<br />

insert into parent(1, '12345');<br />

insert into parent(2, '12346');<br />

insert into parent(3, '12347');<br />

insert into parent(4, '12348');<br />

insert into child (1,'12346');<br />

insert into child (2,'12347');<br />

insert into child (2,'12348');<br />

I would like to pass in a id (i.e. 12345)<br />

and find all his child (1,'12346')<br />

Then <strong>for</strong> each child find his children (1,'12347') and so on down <strong>the</strong> tree.<br />

Like <strong>the</strong> following:<br />

12345<br />

12346<br />

12347<br />

12348<br />

Followup October 14, 2004 - 7pm Central time zone:<br />

what have you tried so far.......<br />

wacky structure to store parent/child in don't you think? probably could find a less efficient<br />

structure <strong>for</strong> a hierarchy....<br />

October 15, 2004 - 8am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Vinnie from Orlando<br />

create table parent( rowid number(2) primary key, id varchar2(14) );<br />

create table child( rowid_parent number(2), id varchar2(14) );<br />

insert into parent(1, '12345');<br />

insert into parent(2, '12346');<br />

insert into parent(3, '12347');<br />

insert into parent(4, '12348');<br />

insert into child (1,'12346');<br />

insert into child (2,'12347');<br />

insert into child (2,'12348');<br />

I have tried <strong>the</strong> following:<br />

select a.id from parent a, child b<br />

start with a.id='12345'<br />

<strong>connect</strong> <strong>by</strong> a.rowid = b.rowid_parent;<br />

Was hoping this could be accomplised using this type of approach....somehow!!


Followup October 15, 2004 - 11am Central time zone:<br />

do you have to live with this "structure"? ugh. it hurts my head to look at it. <strong>the</strong> names don't<br />

even make sense.<br />

Ugh October 15, 2004 - 2pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Vinnie from Orlando<br />

Ugh is right, I have to live with <strong>the</strong> structure!<br />

Perhaps I can explain this better.<br />

I have table EMP with a list of emplyees, and table SUB with each employees subordinates. What I<br />

need is a list of all <strong>the</strong> subordinates <strong>for</strong> a given EMP rolled up to include all subordinate<br />

emplyees. So if I select employee SMITH I get:<br />

SMITH<br />

ADAMS<br />

JONES<br />

ARMSTRONG<br />

FRANK<br />

JAMES<br />

BROWN<br />

But The structure is still <strong>the</strong> same as described be<strong>for</strong>e.<br />

CREATE TABLE EMP (row_id number, ename varchar2(30));<br />

CREATE TABLE SUB (row_id_parent number, ename varchar2(30));<br />

Just assume <strong>the</strong> names are unique <strong>for</strong> this test case.<br />

INSERT INTO EMP (1,'SMITH');<br />

INSERT INTO EMP (2,'ADAMS');<br />

INSERT INTO EMP (3,'JONES');<br />

INSERT INTO EMP (4,'ARMSTRONG');<br />

INSERT INTO EMP (5,'FRANK');<br />

INSERT INTO EMP (6,'JAMES');<br />

INSERT INTO EMP (7,'BROWN');<br />

INSERT INTO SUB (1,'ADAMS');<br />

INSERT INTO SUB (1,'BROWN');<br />

INSERT INTO SUB (2,'JONES');<br />

INSERT INTO SUB (2,'ARMSTONG');<br />

INSERT INTO SUB (2,'JAMES');<br />

INSERT INTO SUB (4,'FRANK');<br />

Hope this helps with your head ache:)<br />

Followup October 15, 2004 - 5pm Central time zone:<br />

ops$tkyte@ORA9IR2> select rpad('*',2*level,'*') || ename name<br />

2 from (select row_id_parent, ename, (select row_id from emp where ename = sub.ename) row_id<br />

from sub)<br />

3 start with row_id_parent = ( select row_id from emp where ename = 'SMITH' )<br />

4 <strong>connect</strong> <strong>by</strong> prior row_id = row_id_parent<br />

5 /<br />

NAME<br />

------------------------------<br />

**ADAMS<br />

****JONES<br />

****ARMSTRONG<br />

******FRANK<br />

****JAMES<br />

**BROWN<br />

6 rows selected.<br />

I figure "smith" can be "implied", you could union him in, but you already sort of know "smith"


GREAT October 18, 2004 - 1pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Vinnie from ORlando<br />

This works great!!<br />

Can you explain this all in plain text?<br />

Followup October 18, 2004 - 2pm Central time zone:<br />

select row_id_parent, ename,<br />

(select row_id from emp where ename = sub.ename) row_id<br />

from sub<br />

gives us <strong>the</strong> single table with <strong>the</strong> parent/child info we need. we needed <strong>the</strong> row_id_parent and<br />

row_id toge<strong>the</strong>r, <strong>the</strong>n <strong>connect</strong> <strong>by</strong> is trivial.<br />

Able to show only certain hierarchies? October 21, 2004 - 12pm Central time zone<br />

Bookmark | Bottom | Top<br />

Reviewer: Jon from CT, USA<br />

Is it possible to only show certain hierarchies that match a WHERE condition? For example in <strong>the</strong><br />

data below, FRENCH reports to both DAVIS AND BLAKE. I would like to show ONLY those hierarchies.<br />

drop table t1;<br />

create table t1 (EMP varchar2(30), MGR varchar2(30));<br />

insert into t1 values ('ADAMS',null);<br />

insert into t1 values ('BLAKE','ADAMS');<br />

insert into t1 values ('CHARLES','ADAMS');<br />

insert into t1 values ('DAVIS','ADAMS');<br />

insert into t1 values ('EDWARDS','BLAKE');<br />

insert into t1 values ('FRENCH','BLAKE');<br />

insert into t1 values ('GAVIN','BLAKE');<br />

insert into t1 values ('HOWARD','CHARLES');<br />

insert into t1 values ('INGRAHAM','CHARLES');<br />

insert into t1 values ('JONES','CHARLES');<br />

insert into t1 values ('KING','DAVIS');<br />

insert into t1 values ('LEWIS','DAVIS');<br />

-- FRENCH REPORTS TO BOTH DAVIS AND BLAKE<br />

insert into t1 values ('FRENCH','DAVIS');<br />

insert into t1 values ('MATTHEWS','FRENCH');<br />

insert into t1 values ('NEWMAN','FRENCH');<br />

COMMIT;<br />

SELECT substr(LPAD(' ',2*(LEVEL - 1))||EMP,1,40) Employee<br />

from t1<br />

<strong>connect</strong> <strong>by</strong> prior<br />

emp = mgr<br />

start with mgr is null;<br />

This results in<br />

EMPLOYEE<br />

----------------------------------------<br />

ADAMS<br />

BLAKE<br />

EDWARDS<br />

FRENCH<br />

MATTHEWS<br />

NEWMAN<br />

GAVIN<br />

CHARLES<br />

HOWARD<br />

INGRAHAM<br />

JONES<br />

DAVIS<br />

KING<br />

LEWIS<br />

FRENCH<br />

MATTHEWS<br />

NEWMAN


NEWMAN<br />

I would like it to only show:<br />

EMPLOYEE<br />

----------------------------------------<br />

ADAMS<br />

BLAKE<br />

EDWARDS<br />

FRENCH<br />

MATTHEWS<br />

NEWMAN<br />

GAVIN<br />

DAVIS<br />

KING<br />

LEWIS<br />

FRENCH<br />

MATTHEWS<br />

NEWMAN<br />

Is that possible<br />

Followup October 21, 2004 - 3pm Central time zone:<br />

is it possible <strong>for</strong> <strong>the</strong>re to be multiple "roots" in this? or will french always roll up to a single<br />

root node?<br />

<strong>Thanks</strong> <strong>for</strong> responding October 21, 2004 - 3pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Jon from CT, USA<br />

There could be multiple roots. How would <strong>the</strong> answer differ if <strong>the</strong> answer was a single root? I ask<br />

because it may be possible to create a view that makes <strong>the</strong> multiple roots all point to a single<br />

(new) root, if that would make this more doable.<br />

Followup October 22, 2004 - 3pm Central time zone:<br />

if <strong>the</strong>re were one root, we could "start with" using this:<br />

ops$tkyte@ORA9IR2> select level, sys_<strong>connect</strong>_<strong>by</strong>_path( emp, '/' ) scbp<br />

2 from t1<br />

3 start with emp = 'FRENCH'<br />

4 <strong>connect</strong> <strong>by</strong> prior mgr = emp<br />

5 /<br />

LEVEL SCBP<br />

---------- -------------------------<br />

1 /FRENCH<br />

2 /FRENCH/BLAKE<br />

3 /FRENCH/BLAKE/ADAMS<br />

1 /FRENCH<br />

2 /FRENCH/DAVIS<br />

3 /FRENCH/DAVIS/ADAMS<br />

6 rows selected.<br />

ops$tkyte@ORA9IR2> select max( to_char(level,'fm00009') || ' ' || sys_<strong>connect</strong>_<strong>by</strong>_path( emp, '/' ) )<br />

scbp<br />

2 from t1<br />

3 start with emp = 'FRENCH'<br />

4 <strong>connect</strong> <strong>by</strong> prior mgr = emp<br />

5 /<br />

SCBP<br />

-------------------------<br />

00003 /FRENCH/DAVIS/ADAMS<br />

see how we could get ADAMS... but if <strong>the</strong>re are multiple roots that each take a different number<br />

of levels to get to -- that would be a problem


with one root, you would START WITH EMP = ( SELECT that root )<br />

but in hindsight -- i see that would not work ei<strong>the</strong>r. We'd have to actually run a <strong>connect</strong> <strong>by</strong> query<br />

per row -- just to see if french was in <strong>the</strong> hierarchy up or down <strong>the</strong> tree. it'd be very expensive.<br />

I'd probably ra<strong>the</strong>r run two queries and may union all <strong>the</strong>m toge<strong>the</strong>r -- one that runs "up" <strong>the</strong> tree<br />

from french, ano<strong>the</strong>r that runs "down <strong>the</strong> tree" from french.<br />

Fur<strong>the</strong>r explanation October 21, 2004 - 3pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Jon from CT, USA<br />

The following demostrates what I meant <strong>by</strong> creating a view to point multiple roots to a new root:<br />

drop table t1;<br />

create table t1 (EMP varchar2(30), MGR varchar2(30));<br />

insert into t1 values ('ADAMS',null);<br />

insert into t1 values ('BLAKE','ADAMS');<br />

insert into t1 values ('CHARLES','ADAMS');<br />

insert into t1 values ('DAVIS','ADAMS');<br />

insert into t1 values ('EDWARDS','BLAKE');<br />

insert into t1 values ('FRENCH','BLAKE');<br />

insert into t1 values ('GAVIN','BLAKE');<br />

insert into t1 values ('HOWARD','CHARLES');<br />

insert into t1 values ('INGRAHAM','CHARLES');<br />

insert into t1 values ('JONES','CHARLES');<br />

insert into t1 values ('KING','DAVIS');<br />

insert into t1 values ('LEWIS','DAVIS');<br />

-- FRENCH REPORTS TO BOTH DAVIS AND BLAKE<br />

insert into t1 values ('FRENCH','DAVIS');<br />

insert into t1 values ('MATTHEWS','FRENCH');<br />

insert into t1 values ('NEWMAN','FRENCH');<br />

insert into t1 values('OLIVER',NULL);<br />

-- FRENCH ALSO REPORTS TO OLIVER<br />

insert into t1 values('FRENCH','OLIVER');<br />

drop view view_t1;<br />

create view view_t1 as<br />

select '.' EMP,null MGR from dual<br />

union<br />

select emp, nvl(mgr, '.') MGR from t1;<br />

SELECT substr(LPAD(' ',2*(LEVEL - 1))||EMP,1,40) Employee<br />

from t1<br />

<strong>connect</strong> <strong>by</strong> prior<br />

emp = mgr<br />

start with mgr is null;<br />

SELECT substr(LPAD(' ',2*(LEVEL - 1))||EMP,1,40) Employee<br />

from view_t1<br />

<strong>connect</strong> <strong>by</strong> prior<br />

emp = mgr<br />

start with mgr is null;<br />

To Jon ... October 22, 2004 - 4pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Gabe<br />

So ... how should your report look if FRENCH who now reports to DAVIS gets to lead a new project<br />

having DAVIS as a resource (it frequently happens in real life)?<br />

Maybe hierarchical queries are not quite applicable to your _model_. They work on hierarchies ...<br />

don't work very well on graphs.<br />

Followup October 23, 2004 - 9am Central time zone:<br />

(in 10g with NOCYCLE and isleaf and o<strong>the</strong>r new functions -- <strong>the</strong>y will work much much better with<br />

graphs)


Not quite <strong>the</strong>re yet October 25, 2004 - 9am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Jon from Jon, CT, USA<br />

It may be intuitively obvious to you, but I'm struggling with a query that will give me <strong>the</strong> results<br />

I need using your "two query - one up, one down - union all" suggestion. I need <strong>the</strong> query to<br />

return <strong>the</strong> following results:<br />

Level Emp<br />

1 ADAMS<br />

2 BLAKE<br />

3 FRENCH<br />

4 MATTHEWS<br />

4 NEWMAN<br />

2 DAVIS<br />

3 FRENCH<br />

4 MATTHEWS<br />

4 NEWMAN<br />

1 OLIVER<br />

2 FRENCH<br />

3 MATTHEWS<br />

3 NEWMAN<br />

Also, although we're not at 10g yet, if that has a more straight<strong>for</strong>ward solution, I'd be interested<br />

in seeing <strong>the</strong> solution.<br />

Followup October 25, 2004 - 11am Central time zone:<br />

you'll get <strong>the</strong> output of two queries -- unioned toge<strong>the</strong>r. It will not be exactly like that above<br />

-- it'll be <strong>the</strong> data you need however --<br />

ops$tkyte@ORA9IR2> select level, rpad('*',2*level,'*') || emp ename<br />

2 from t1<br />

3 start with emp = 'FRENCH'<br />

4 <strong>connect</strong> <strong>by</strong> prior mgr = emp<br />

5 /<br />

LEVEL ENAME<br />

---------- ------------------------------<br />

1 **FRENCH<br />

2 ****BLAKE<br />

3 ******ADAMS<br />

1 **FRENCH<br />

2 ****DAVIS<br />

3 ******ADAMS<br />

1 **FRENCH<br />

2 ****OLIVER<br />

8 rows selected.<br />

ops$tkyte@ORA9IR2><br />

ops$tkyte@ORA9IR2> select level, rpad('*',2*level,'*') || emp ename<br />

2 from t1<br />

3 start with mgr = 'FRENCH'<br />

4 <strong>connect</strong> <strong>by</strong> prior emp = mgr<br />

5 /<br />

LEVEL ENAME<br />

---------- ------------------------------<br />

1 **MATTHEWS<br />

1 **NEWMAN<br />

that is what you'll have to work with if you want just "french"<br />

November 22, 2004 - 4am Central time zone Bookmark | Bottom | Top<br />

Reviewer: A reader<br />

How to skip <strong>the</strong> self reference November 29, 2004 - 5am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Ara


ORACLE : 8i<br />

Problem: Connect-<strong>by</strong> because self-reference<br />

Hi Tom,<br />

Is <strong>the</strong>re any way to skip <strong>the</strong> self-reference.<br />

I need all <strong>the</strong> records be<strong>for</strong>e self-reference. Suppose my table has 1000000 records, <strong>the</strong>re is a<br />

self-referece at 50000th records. why should my query fail?<br />

is <strong>the</strong>re any way to skip <strong>the</strong> this records and move fur<strong>the</strong>r.<br />

<strong>Thanks</strong><br />

Followup November 29, 2004 - 8am Central time zone:<br />

in 10g <strong>the</strong>re is a "NOCYCLE" operation. to detect a <strong>connect</strong> <strong>by</strong> loop prior to that was not part of<br />

<strong>the</strong> functionality of <strong>connect</strong> <strong>by</strong>.<br />

<strong>connect</strong> <strong>by</strong> in view December 1, 2004 - 10am Central time zone Bookmark | Bottom | Top<br />

Reviewer: A reader<br />

i am refering toms comment:<br />

"see how we could get ADAMS... but if <strong>the</strong>re are multiple roots that each take a<br />

different number of levels to get to -- that would be a problem"<br />

we are exactky in that situation, we wannt a <strong>connect</strong> <strong>by</strong>, and have a root-node column returned<br />

select :x, ...<br />

[...]<br />

start with :x<br />

would do <strong>the</strong> job, but we need <strong>the</strong> functionality in a view, so we can't use <strong>the</strong> start with clause.<br />

one solution would be to<br />

select substr(sys_<strong>connect</strong>_<strong>by</strong>_path, 1, 14) RNODE, ...<br />

(<strong>the</strong> column is char(14))<br />

<strong>the</strong> problem is <strong>the</strong>re is a bug with sys_<strong>connect</strong>_<strong>by</strong>_path when used in a query.<br />

select RNODE friom viewxyz; works but using RNODE in a where clause raises an ORA-600<br />

<strong>the</strong> problem is, we currently can't update.<br />

maybe someone has a different solution?<br />

Regarding Mita's request (08/24/2004) February 11, 2005 - 4am Central time zone<br />

Bookmark | Bottom | Top<br />

Reviewer: Jet-Lagged Jim from Vancouver<br />

Hi Tom.<br />

So if we wanted a third column in <strong>the</strong> result set that corresponds to <strong>the</strong> hierarchical level of each<br />

manager's employees, how would that be done? Tried several things, but can't quite seem to "get<br />

it".<br />

example output:<br />

emp_id top_mgr hier_level<br />

------- -------- -----------<br />

1 4 4<br />

2 4 3<br />

3 4 2<br />

4 4 1<br />

5 7 3<br />

6 7 2<br />

7 7 1<br />

<strong>Thanks</strong> so much!


From Jet-Lagged Jim February 11, 2005 - 1pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Jim from Vancouver<br />

Regading my above inquiry - please disregard, I figured it out. T'was a muddled-brain posting at<br />

1:00am from a handful of timezones. Feel free to remove. <strong>Thanks</strong> again.<br />

Followup February 12, 2005 - 8am Central time zone:<br />

don't sweat it, i just got back last night 6 hours off myself :)<br />

2 Tables in using start with , <strong>connect</strong> <strong>by</strong> May 9, 2005 - 3pm Central time zone<br />

Bookmark | Bottom | Top<br />

Reviewer: Lamya from Houston,TX<br />

I have 2 tables<br />

CREATE TABLE HYBRIDOMA<br />

(<br />

HYBRIDOMA_ID NUMBER(10),<br />

HYBRIDOMA_NAME VARCHAR2(20 BYTE),<br />

)<br />

CREATE TABLE CLONE<br />

(<br />

CLONE_ID NUMBER(10),<br />

CLONE_NAME VARCHAR2(60 BYTE),<br />

PARENT_TYPE VARCHAR2(10 BYTE),<br />

PARENT_ID NUMBER(22)<br />

)<br />

INSERT INTO CLONE ( CLONE_ID, CLONE_NAME, PARENT_TYPE, PARENT_ID ) VALUES (<br />

1, 'clone_from_h1', 'HYBRID', 1);<br />

INSERT INTO CLONE ( CLONE_ID, CLONE_NAME, PARENT_TYPE, PARENT_ID ) VALUES (<br />

2, 'clone_from_clone1', 'CLONE', 1);<br />

INSERT INTO CLONE ( CLONE_ID, CLONE_NAME, PARENT_TYPE, PARENT_ID ) VALUES (<br />

3, 'clone_from_h1', 'HYBRID', 1);<br />

commit;<br />

INSERT INTO HYBRIDOMA ( HYBRIDOMA_ID, HYBRIDOMA_NAME ) VALUES (<br />

0, 'fff');<br />

INSERT INTO HYBRIDOMA ( HYBRIDOMA_ID, HYBRIDOMA_NAME ) VALUES (<br />

1, 'rrrrr');<br />

INSERT INTO HYBRIDOMA ( HYBRIDOMA_ID, HYBRIDOMA_NAME ) VALUES (<br />

2, 'ddddddd');<br />

commit;<br />

Now <strong>the</strong> clone table has parent type = clone or hybrid. thus <strong>the</strong> clone can have parents in <strong>the</strong><br />

hybridoma table.<br />

I would like to create a select statement which would start on <strong>the</strong> top and select all children ,<br />

from both hybridoma and clone .<br />

I tried this but its not helping me .<br />

select level , lpad('*',level*2,'*') || decode( parent_type ,'CLONE' , clone_name , 'HYBRID' ,<br />

hybridoma_name)<br />

from clone c , hybridoma h<br />

where c.parent_id = h.HYBRIDOMA_ID<br />

start with c.parent_id = 1<br />

<strong>connect</strong> <strong>by</strong> prior c.clone_id = c.parent_id and clone_ID parent_id<br />

zone<br />

Very In<strong>for</strong>mative - but can you get only a portion of <strong>the</strong> hierarchy? January 30, 2006 - 7pm Central time<br />

Reviewer: Marshall B Thompson from Charlotte, NC, USA<br />

Bookmark | Bottom | Top


Reviewer: Marshall B Thompson from Charlotte, NC, USA<br />

late to <strong>the</strong> party, I know, but I find this thread very in<strong>for</strong>mative. Back to <strong>the</strong> very original<br />

example you used at <strong>the</strong> top of <strong>the</strong> thread, you produced <strong>the</strong> results:<br />

ENAME EMPNO MGR<br />

--------------- ---------- ----------<br />

KING 7839<br />

JONES 7566 7839<br />

SCOTT 7788 7566<br />

ADAMS 7876 7788<br />

FORD 7902 7566<br />

SMITH 7369 7902<br />

BLAKE 7698 7839<br />

ALLEN 7499 7698<br />

WARD 7521 7698<br />

MARTIN 7654 7698<br />

TURNER 7844 7698<br />

JAMES 7900 7698<br />

CLARK 7782 7839<br />

MILLER 7934 7782<br />

What if you had <strong>the</strong> employee numbers <strong>for</strong> allen, ward, martin, turner, james, and miller, and wanted<br />

to get <strong>the</strong> results below. (The hierarchy above just those employees.) How would that be done?<br />

ENAME EMPNO MGR<br />

--------------- ---------- ----------<br />

KING 7839<br />

BLAKE 7698 7839<br />

ALLEN 7499 7698<br />

WARD 7521 7698<br />

MARTIN 7654 7698<br />

TURNER 7844 7698<br />

JAMES 7900 7698<br />

CLARK 7782 7839<br />

MILLER 7934 7782<br />

Followup January 31, 2006 - 1am Central time zone:<br />

and what if martin was not in <strong>the</strong> list?<br />

I must be missing something January 31, 2006 - 8am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Marshall B Thompson from Charlotte, NC USA<br />

From your response, this must be obvious, but is not to me at <strong>the</strong> moment. (But, I would expecte<br />

Martin to not be in <strong>the</strong> output, but all else <strong>the</strong> same.)<br />

Against my HR sample schema, running <strong>the</strong> following query:<br />

select lpad(' ',level*2,' ')||last_name ename, employee_id, manager_id<br />

from hr.employees<br />

start with manager_id is null<br />

<strong>connect</strong> <strong>by</strong> prior employee_id = manager_id<br />

I get:<br />

King<br />

100<br />

Kochhar<br />

101 100<br />

Greenberg<br />

108 101<br />

Faviet<br />

109 108<br />

Chen<br />

110 108<br />

Sciarra<br />

111 108


111 108<br />

Urman<br />

112 108<br />

Popp<br />

113 108<br />

Whalen<br />

200 101<br />

Mavris<br />

203 101<br />

................etc.<br />

Given employee id's 111 (Sciarra) and 112 (Urman), I'd like to get <strong>the</strong> relevant hierarchy from <strong>the</strong>m<br />

up. Desired results would be:<br />

King<br />

100<br />

Kochhar<br />

101 100<br />

Greenberg<br />

108 101<br />

Sciarra<br />

111 108<br />

Urman<br />

112 108<br />

But, when I do this:<br />

select lpad(' ',level*2,' ')||last_name ename, employee_id, manager_id<br />

from hr.employees<br />

where employee_id in (111, 112)<br />

start with manager_id is null<br />

<strong>connect</strong> <strong>by</strong> prior employee_id = manager_id<br />

I wind up with:<br />

Sciarra<br />

111 108<br />

Urman<br />

112 108<br />

How can I accomplish <strong>the</strong> desired results?<br />

Ahhhhhh January 31, 2006 - 10am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Marshall B Thompson from Charlotte, NC USA<br />

found my inspiration here:<br />

http://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:13129005201417<br />

select lpad(' ',level*2,' ')||last_name ename, employee_id, manager_id<br />

from hr.employees<br />

start with employee_id in (111, 112)<br />

<strong>connect</strong> <strong>by</strong> prior manager_id = employee_id<br />

Sciarra<br />

111 108<br />

Greenberg<br />

108 101<br />

Kochhar<br />

101 100<br />

King<br />

100<br />

Urman<br />

112 108


Greenberg<br />

108 101<br />

Kochhar<br />

101 100<br />

King<br />

100<br />

Followup January 31, 2006 - 3pm Central time zone:<br />

I did not mean to make you think this was "obvious", just that <strong>the</strong> answer would have to be very<br />

different if "martin" (or indeed any of <strong>the</strong> leaves) were not to be in <strong>the</strong> output!<br />

Connect <strong>by</strong> prior February 28, 2006 - 9am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Sanjeev Vibuthi from Hyderabad, India<br />

Hi Tom,<br />

This is my table... I have set all reports_to correctly.. But <strong>the</strong> rows are repeating when I<br />

executed <strong>the</strong> following query...<br />

desc test_unit<br />

Name Null? Type<br />

----------------------------------------------------------------- -------- ------------------------<br />

D_CD NOT NULL NUMBER(3)<br />

U_CD NOT NULL NUMBER(3)<br />

U_NAME VARCHAR2(50)<br />

U_TYPE VARCHAR2(10)<br />

REPORTS_TO NUMBER(3)<br />

SELECT SUBSTR(lpad(' ',Level*2,' '),1,30)||U_name Name,<br />

U_Cd, Repors_To<br />

From test_unit<br />

where d_cd=32<br />

start with d_cd=32 and u_cd=500<br />

<strong>connect</strong> <strong>by</strong> prior u_cd = repors_to<br />

--d_cd=32 where 32 is Top Level<br />

NAME U_CD REPORS_TO<br />

-------------------------------------------------------------------------------- ----------<br />

------------<br />

Central - NY 500 0<br />

EZ 301*<br />

500<br />

EZ-SDivision 202<br />

301*<br />

Sdivision2 13<br />

202<br />

Sdivision3 70<br />

202<br />

EZ-KDivision 203<br />

301*<br />

KDivision2 56<br />

203<br />

KDivision3 59<br />

203<br />

EZ-MDivision 223<br />

301*<br />

MDivision3 68<br />

223<br />

MDivision2 44<br />

223<br />

-- It is repeating (already displayed in "ES_SDivision")<br />

Sdivision2 13<br />

202?<br />

Sdivision3 70<br />

202?<br />

-- The following UNITS should come under "SZ" - "SZ-CR Division"<br />

CR Division-4 37<br />

221***<br />

CR Division-5 88<br />

221


221<br />

221<br />

CR Division-6 92<br />

NZ 302<br />

500<br />

NZ-GP-Division 211<br />

302<br />

GP-Division-3 40<br />

211<br />

GP-Division-4 77<br />

211<br />

NZ-MK-Division 212<br />

302<br />

MK-Division-2 46<br />

212<br />

MK-Division-3 52<br />

212<br />

MK-Division-4 89<br />

212<br />

NZ-BP-Division 213<br />

302<br />

BP-Division-4 84<br />

213<br />

BP-Division-5 91<br />

213<br />

-----The followings UNITS already displayed under "EZ" - "EZ-KDivision "<br />

KDivision2 56<br />

203<br />

KDivision3 59<br />

203<br />

-----The followings UNITS already displayed under "SZ" - "SZ-CK Division "<br />

CK Division-3 71<br />

224<br />

CK Division-4 72<br />

224<br />

-- The following Two Zones are Coming correctly<br />

SZ 303<br />

500<br />

SZ-CR Division 221***<br />

303<br />

CR Division-4 37<br />

221<br />

CR Division-5 88<br />

221<br />

CR Division-6 92<br />

221<br />

SZ-MR Division 222<br />

303<br />

MR Division-4 86<br />

222<br />

MR Division-3 62<br />

222<br />

SZ-CK Division 224<br />

303<br />

CK Division-3 71<br />

224<br />

CK Division-4 72<br />

224<br />

SZ-SR Division 237<br />

303<br />

SR Division-4 85<br />

237<br />

SR Division-3 64<br />

237<br />

WZ 304<br />

500<br />

WZ - AN Division 231<br />

304<br />

AN Division-4 41<br />

231<br />

AN Division-5 74<br />

231<br />

WZ - GL Division 232<br />

304<br />

GL Division-3 45<br />

232


GL Division-4 69<br />

232<br />

WZ - PG Division 234<br />

304<br />

PG Division-1 57<br />

234<br />

PG Division-2 67<br />

234<br />

WZ - BH Division 236<br />

304<br />

BH Division-2 87<br />

236<br />

BH Division-1 11<br />

236<br />

Where is <strong>the</strong> error in my <strong>question</strong>...<br />

<strong>Thanks</strong> in Adv..<br />

Sanjeev Vibuthi<br />

Followup February 28, 2006 - 9am Central time zone:<br />

I don't know.<br />

Why don't I know?<br />

because I don't have anything to reproduce with, no create table, no insert intos, we have no clue<br />

what you original source data looks like at all.<br />

So, who knows.<br />

(can you tell this gets frustrating when day after day no one reads <strong>the</strong> thing that says<br />

.... If your followup requires a response that might include a query, you had better supply very<br />

very simple create tables and insert statements. I cannot create a table and populate it <strong>for</strong> each<br />

and every <strong>question</strong>. The SMALLEST create table possible (no tablespaces, no schema names, just like<br />

I do in my examples <strong>for</strong> you) ...)<br />

February 28, 2006 - 10am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Alex<br />

Especially since you have to check a box saying you agreed. You could probably change that to say<br />

anything and people would just click it. "I really like Michael Bolton he's fantastic...." ;)<br />

Connect <strong>by</strong> March 1, 2006 - 3am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Sanjeev Vibuthi from Hyderabad, India<br />

Hi Tom,<br />

I will explain my problem with EMP Table ...<br />

I added COMPANY_CD column to EMP table and renamed it to TEST_EMP (PK(COMPANY_CD, EMPNO))<br />

Test_Emp table contains company wise Employee Details<br />

SCOTT@ testdb 01-MAR-06>SELECT COMPANY_CD, EMPNO,ENAME,MGR FROM TEST_EMP<br />

2* ORDER BY COMPANY_CD<br />

SCOTT@ testdb 01-MAR-06>/<br />

COMPANY_CD EMPNO ENAME MGR<br />

---------- ---------- ---------- ----------<br />

101 7369 SMITH 7902<br />

7499 ALLEN 7698<br />

7521 WARD 7698<br />

7566 JONES 7839<br />

7654 MARTIN 7698


7698 BLAKE 7839<br />

7782 CLARK 7839<br />

7788 SCOTT 7566<br />

7839 KING<br />

7844 TURNER 7698<br />

7876 ADAMS 7788<br />

7900 JAMES 7698<br />

7902 FORD 7566<br />

7934 MILLER 7782<br />

201 7369 SMITH 7902<br />

7499 ALLEN 7698<br />

7521 WARD 7698<br />

7566 JONES 7839<br />

7654 MARTIN 7698<br />

7698 BLAKE 7839<br />

7782 CLARK 7839<br />

7788 SCOTT 7566<br />

7839 KING<br />

7844 TURNER 7698<br />

7876 ADAMS 7788<br />

7900 JAMES 7698<br />

7902 FORD 7566<br />

7934 MILLER 7782<br />

28 rows selected.<br />

-- I have written <strong>connect</strong> <strong>by</strong> query to get Employee Hierarchy in a Given Company<br />

SCOTT@ testdb 01-MAR-06><br />

1 Select substr(lpad('-',Level*2,'-')||Empno,1,15) Empno,<br />

2 ename From test_emp<br />

3 where company_cd=101<br />

4 start with company_cd=101<br />

5* <strong>connect</strong> <strong>by</strong> prior empno=mgr<br />

SCOTT@ testdb 01-MAR-06>/<br />

EMPNO ENAME<br />

--------------- ----------<br />

--7788 SCOTT<br />

----7876 ADAMS<br />

--7902 FORD<br />

----7369 SMITH<br />

--7499 ALLEN<br />

--7521 WARD<br />

--7654 MARTIN<br />

--7844 TURNER<br />

--7900 JAMES<br />

--7934 MILLER<br />

--7876 ADAMS<br />

--7566 JONES<br />

----7788 SCOTT<br />

------7876 ADAMS<br />

----7902 FORD<br />

------7369 SMITH<br />

------7876 ADAMS<br />

------7369 SMITH<br />

--7698 BLAKE<br />

----7499 ALLEN<br />

----7521 WARD<br />

----7654 MARTIN<br />

----7844 TURNER<br />

.......<br />

.......<br />

.......<br />

55 rows selected. -- But table is having only 24 records<br />

SCOTT@ testdb 01-MAR-06><br />

1 Select substr(lpad('-',Level*2,'-')||Empno,1,15) Empno,<br />

2 ename From (Select * from test_emp where company_cd=101) test_emp -- Inline View<br />

3 start with company_cd=101<br />

4* <strong>connect</strong> <strong>by</strong> prior empno=mgr<br />

SCOTT@ testdb 01-MAR-06>/<br />

EMPNO ENAME


--------------- ----------<br />

--7788 SCOTT<br />

----7876 ADAMS<br />

--7902 FORD<br />

----7369 SMITH<br />

--7499 ALLEN<br />

--7521 WARD<br />

--7654 MARTIN<br />

--7844 TURNER<br />

--7900 JAMES<br />

--7934 MILLER<br />

--7876 ADAMS<br />

--7566 JONES<br />

----7788 SCOTT<br />

------7876 ADAMS<br />

----7902 FORD<br />

------7369 SMITH<br />

--7698 BLAKE<br />

----7499 ALLEN<br />

----7521 WARD<br />

----7654 MARTIN<br />

......<br />

......<br />

39 rows selected. -- Still I got more records<br />

SCOTT@ testdb 01-MAR-06><br />

1 Select substr(lpad('-',Level*2,'-')||Empno,1,15) Empno,<br />

2 ename From (Select * from test_emp where company_cd=101) test_emp<br />

3 start with mgr is null -- Change condition<br />

4* <strong>connect</strong> <strong>by</strong> prior empno=mgr<br />

SCOTT@ testdb 01-MAR-06>/<br />

EMPNO ENAME<br />

--------------- ----------<br />

--7839 KING<br />

----7566 JONES<br />

------7788 SCOTT<br />

--------7876 ADAMS<br />

------7902 FORD<br />

--------7369 SMITH<br />

----7698 BLAKE<br />

------7499 ALLEN<br />

------7521 WARD<br />

------7654 MARTIN<br />

------7844 TURNER<br />

------7900 JAMES<br />

----7782 CLARK<br />

------7934 MILLER<br />

14 rows selected.<br />

I got what i want, but is <strong>the</strong>re any o<strong>the</strong>r way to do this..<br />

Even though my doublt is ... in From clause I have used inline view which contains only 14 records<br />

but result shows 39 records... Why <strong>the</strong>y are repeating.. If parent key is combination of company_cd,<br />

mgr <strong>the</strong>n<br />

how to write <strong>connect</strong> <strong>by</strong> clause<br />

Thans in Adv.<br />

Sanjeev Vibuthi<br />

Followup March 1, 2006 - 8am Central time zone:<br />

well, if you don't have "start with", you start with EACH RECORDS (meaning - <strong>the</strong>re are 14 emps,<br />

you'll have 14 trees)<br />

so, what do you want to start with exactly<br />

They are NOT repeating, you asked <strong>for</strong> <strong>the</strong>m all - you have 14 "trees"


March 1, 2006 - 9am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Tom Fox from Cincinnati, OH<br />

Tom, so what I'm getting out of <strong>the</strong> above query (repeating, not repeating, whatever) is that <strong>the</strong><br />

START WITH clause _must_ reference <strong>the</strong> left hand side of <strong>the</strong> CONNECT BY clause. Is this correct?<br />

--Tom<br />

Followup March 1, 2006 - 9am Central time zone:<br />

I don't even know what <strong>the</strong> "left hand side" of a <strong>connect</strong> <strong>by</strong> would be, so no.<br />

start with identifies <strong>the</strong> set of ROOT NODES.<br />

<strong>connect</strong> <strong>by</strong> builds a hierarchy of child nodes under each root.<br />

March 1, 2006 - 9am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Tom Fox from Cincinnati, OH<br />

Yeah, I haven't finished my coffee yet. I meant <strong>the</strong> right hand side in my post above.<br />

--Tom<br />

Followup March 1, 2006 - 9am Central time zone:<br />

I don't know what <strong>the</strong> right hand side is ei<strong>the</strong>r.<br />

March 1, 2006 - 5pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Tom Fox from Cincinnati, OH<br />

Oh, you know, <strong>the</strong> right hand side.... Work with me here. :)<br />

In this query:<br />

scott@ORA8I.WORLD> select lpad(' ',level*2,' ')||ename ename, empno, mgr<br />

2 from emp<br />

3 START WITH MGR IS NULL<br />

4 CONNECT BY PRIOR EMPNO = MGR<br />

5 /<br />

What I was trying to ask: Does <strong>the</strong> START WITH clause (meaning MGR on line 3) have to reference <strong>the</strong><br />

right side of <strong>the</strong> CONNECT BY clause (line 4, in this case), in order <strong>for</strong> <strong>the</strong> tree to be <strong>for</strong>med<br />

correctly?<br />

In o<strong>the</strong>r terms, does START WITH always have to reference one of <strong>the</strong> fields in <strong>the</strong> CONNECT BY<br />

clause?<br />

Followup March 2, 2006 - 9am Central time zone:<br />

No, it does not have to.<br />

your start with simply identifies <strong>the</strong> rows that start hierarchies.<br />

You might want a tree <strong>for</strong> every MGR that has <strong>the</strong> JOB = 'X'<br />

so <strong>the</strong> start with would be referencing <strong>the</strong> EMPNO column and <strong>the</strong> JOB column (to find empnos that are<br />

mgrs and have a job = 'X' )<br />

Can we add salary to <strong>the</strong> query? March 2, 2006 - 1pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Mike from Dallas, TX<br />

Hi Tom,


Could you show how to add Salary to this query using <strong>the</strong> <strong>connect</strong> <strong>by</strong>? <strong>for</strong> example I would like to<br />

add a column to <strong>the</strong> query that shows <strong>the</strong> total salary <strong>for</strong> each manager and his/her employees<br />

including all employees down to <strong>the</strong> last leaf.<br />

In you example above your output shows<br />

ENAME EMPNO MGR<br />

--------------- ---------- ----------<br />

KING 7839<br />

JONES 7566 7839<br />

SCOTT 7788 7566<br />

ADAMS 7876 7788<br />

FORD 7902 7566<br />

SMITH 7369 7902<br />

In o<strong>the</strong>r words <strong>the</strong> overall salary column <strong>for</strong> King would be <strong>the</strong> sum of<br />

KING+JONES+SCOTT+ADAMS+FORD+SMITH because <strong>the</strong>y all fall under KING or an employee of KING, <strong>the</strong><br />

overall salary column <strong>for</strong> SCOTT would be SCOTT's salary + ADAMS and FORD's would be FORD's salary +<br />

SMITH's<br />

Of course SMITH's and ADAM's salaries would only include <strong>the</strong>irs because <strong>the</strong>y don't manage anyone.<br />

Any help would be greatly appreciated<br />

Followup March 2, 2006 - 2pm Central time zone:<br />

ops$tkyte@ORA9IR2> select rpad('*',2*level,'*')||ename nm, empno, mgr,<br />

2 (select sum(sal) from emp e2 start with e2.empno = emp.empno <strong>connect</strong> <strong>by</strong> prior empno =<br />

mgr) sum_sal<br />

3 from emp<br />

4 start with mgr is null<br />

5 <strong>connect</strong> <strong>by</strong> prior empno = mgr;<br />

NM EMPNO MGR SUM_SAL<br />

-------------------- ---------- ---------- ----------<br />

**KING 7839 29025<br />

****JONES 7566 7839 10875<br />

******SCOTT 7788 7566 4100<br />

********ADAMS 7876 7788 1100<br />

******FORD 7902 7566 3800<br />

********SMITH 7369 7902 800<br />

****BLAKE 7698 7839 9400<br />

******ALLEN 7499 7698 1600<br />

******WARD 7521 7698 1250<br />

******MARTIN 7654 7698 1250<br />

******TURNER 7844 7698 1500<br />

******JAMES 7900 7698 950<br />

****CLARK 7782 7839 3750<br />

******MILLER 7934 7782 1300<br />

14 rows selected.<br />

Using multiple table join and <strong>connect</strong> <strong>by</strong> March 10, 2006 - 3pm Central time zone<br />

Bookmark | Bottom | Top<br />

Reviewer: Mike from Dallas, TX<br />

Tom,..<br />

<strong>Thanks</strong> <strong>for</strong> <strong>the</strong> quick reply, I apologize <strong>for</strong> not getting back to you quickly.<br />

Maybe I didn't explain well enough. a scalar subquery work very well but I am concerned about <strong>the</strong><br />

table growing and causing <strong>the</strong> query to become very taxing to <strong>the</strong> system.<br />

What we have is a table of entities and a table of entity tickets, <strong>the</strong>y are<br />

CREATE TABLE ENTITY (<br />

ENTITY_UUID VARCHAR2(32),<br />

NAME VARCHAR2(256),<br />

PARENT_UUID VARCHAR2(32)<br />

)<br />

/


Table created.<br />

CREATE TABLE ENTITY_TCKT (<br />

ENTITY_UUID VARCHAR2(32),<br />

CURRENT_LIFECYCLE_STATE NUMBER<br />

)<br />

/<br />

To populate <strong>the</strong>se tables and show some stats,..<br />

insert into entity values ('13E7CAA5FDEB42518A798A77A19F70B0','Level1<br />

Entity',NULL);<br />

insert into entity values ('66A6A6EFFA9D46BE82EC8F5CFFAC91B9','Level4<br />

Entity','536FCF7E4A5D457B8C3AECBED878FDBF');<br />

insert into entity values ('DCF6B6366D6449DB95A5AEA6B14F31F7','Level5<br />

Entity','66A6A6EFFA9D46BE82EC8F5CFFAC91B9');<br />

insert into entity values ('E2FD444948714528805EBFFA102511F5','Level5<br />

Entity','CB4E1B74035947B9A5B9B0FE264DF4E7');<br />

insert into entity values ('2E1E0646AC9F4BB9A6E4A747B20B2595','Level2<br />

Entity','13E7CAA5FDEB42518A798A77A19F70B0');<br />

insert into entity values ('54133391FDD54221B11382A20DFC38AA','Level2<br />

Entity','13E7CAA5FDEB42518A798A77A19F70B0');<br />

insert into entity values ('95A5F85D68184DB7A49F9DF7A236F9AF','Level5<br />

Entity','99762DC75A5D42DCBEA6950D7011F130');<br />

insert into entity values ('EAD30C5578BD491991B0D7049CD4F277','Level4<br />

Entity','536FCF7E4A5D457B8C3AECBED878FDBF');<br />

insert into entity values ('536FCF7E4A5D457B8C3AECBED878FDBF','Level3<br />

Entity','54133391FDD54221B11382A20DFC38AA');<br />

insert into entity values ('883FD970DF264B7A9DC0DFBAF225012A','Level4<br />

Entity','536FCF7E4A5D457B8C3AECBED878FDBF');<br />

insert into entity values ('C23E104B5AA044F795A6896B1C0B08E4','Level3<br />

Entity','54133391FDD54221B11382A20DFC38AA');<br />

insert into entity values ('64BA9F3295194A3B955FD446DBB2E7EC','Level4<br />

Entity','C23E104B5AA044F795A6896B1C0B08E4');<br />

insert into entity values ('0FE28D72C40C4D6FBB439A49B0BE6D3F','Level5<br />

Entity','64BA9F3295194A3B955FD446DBB2E7EC');<br />

----------list goes on------------------<br />

Then to generate some random ticket data<br />

DECLARE<br />

l_tckt_ktr NUMBER;<br />

l_entity_ktr NUMBER;<br />

BEGIN<br />

FOR i IN (select entity_uuid from entity)<br />

LOOP<br />

l_entity_ktr := round(dbms_random.value(1,6),0);<br />

DBMS_OUTPUT.PUT_LINE('Processing entity -> '||i.entity_uuid||'<br />

l_entity_ktr = '||l_entity_ktr);<br />

IF l_entity_ktr = 1 THEN<br />

l_tckt_ktr := round(dbms_random.value(1,10),0);<br />

FOR n IN 1 .. l_tckt_ktr<br />

LOOP<br />

insert into entity_tckt values (i.entity_uuid,0);<br />

END LOOP;<br />

ELSIF l_entity_ktr = 3 THEN<br />

l_tckt_ktr := round(dbms_random.value(50,100),0);<br />

FOR n IN 1 .. l_tckt_ktr<br />

LOOP<br />

insert into entity_tckt values (i.entity_uuid,0);<br />

END LOOP;<br />

ELSIF l_entity_ktr = 5 THEN<br />

l_tckt_ktr := round(dbms_random.value(500,1000),0);<br />

FOR n IN 1 .. l_tckt_ktr<br />

LOOP<br />

insert into entity_tckt values (i.entity_uuid,0);<br />

END LOOP;<br />

END IF;<br />

END LOOP;<br />

END;<br />

/<br />

PL/SQL procedure successfully completed.<br />

Now that <strong>the</strong> tables have data, try two ways to get <strong>the</strong> query<br />

First <strong>the</strong> Scalar Subquery


set lines 256<br />

column lpad('',2*level)||e.name <strong>for</strong>mat a50<br />

column lpad('',2*level)||e.entity_uuid <strong>for</strong>mat a50<br />

select lpad(' ', 2 * level)||e.name, lpad(' ', 2 * level)||e.entity_uuid, level,<br />

(select count(*) from entity_tckt where current_lifecycle_state = 0 and<br />

entity_uuid in (<br />

select a.entity_uuid from entity a start with a.entity_uuid =<br />

e.entity_uuid <strong>connect</strong> <strong>by</strong> prior a.entity_uuid = a.parent_uuid)) as nbr<br />

from entity e<br />

start with e.entity_uuid = '13E7CAA5FDEB42518A798A77A19F70B0' <strong>connect</strong> <strong>by</strong> prior<br />

e.entity_uuid = e.parent_uuid<br />

/<br />

LPAD('',2*LEVEL)||E.NAME<br />

LPAD('',2*LEVEL)||E.ENTITY_UUID LEVEL NBR<br />

--------------------------------------------------<br />

-------------------------------------------------- ---------- ----------<br />

Level1 Entity<br />

13E7CAA5FDEB42518A798A77A19F70B0 1 15533<br />

Level2 Entity<br />

2E1E0646AC9F4BB9A6E4A747B20B2595 2 7<br />

Level2 Entity<br />

54133391FDD54221B11382A20DFC38AA 2 15526<br />

Level3 Entity<br />

536FCF7E4A5D457B8C3AECBED878FDBF 3 890<br />

Level4 Entity<br />

66A6A6EFFA9D46BE82EC8F5CFFAC91B9 4 51<br />

Level5 Entity<br />

DCF6B6366D6449DB95A5AEA6B14F31F7 5 0<br />

Level4 Entity<br />

EAD30C5578BD491991B0D7049CD4F277 4 76<br />

Level4 Entity<br />

883FD970DF264B7A9DC0DFBAF225012A 4 0<br />

Level4 Entity<br />

0F01178489CB421CA5A4C55AEC98300E 4 4<br />

Level5 Entity<br />

5B3B033B4A30443BB1B6F57C1BB05399 5 4<br />

Level4 Entity<br />

CB4E1B74035947B9A5B9B0FE264DF4E7 4 69<br />

LPAD('',2*LEVEL)||E.NAME<br />

LPAD('',2*LEVEL)||E.ENTITY_UUID LEVEL NBR<br />

--------------------------------------------------<br />

-------------------------------------------------- ---------- ----------<br />

Level5 Entity<br />

E2FD444948714528805EBFFA102511F5 5 69<br />

Level4 Entity<br />

99762DC75A5D42DCBEA6950D7011F130 4 607<br />

Level5 Entity<br />

95A5F85D68184DB7A49F9DF7A236F9AF 5 517<br />

-------------------and on and on----------------<br />

122 rows selected.<br />

Execution Plan<br />

----------------------------------------------------------<br />

0 SELECT STATEMENT Optimizer=CHOOSE<br />

1 0 SORT (AGGREGATE)<br />

2 1 FILTER<br />

3 2 TABLE ACCESS (FULL) OF 'ENTITY_TCKT'<br />

4 2 FILTER<br />

5 4 CONNECT BY (WITH FILTERING)<br />

6 5 NESTED LOOPS<br />

7 6 TABLE ACCESS (FULL) OF 'ENTITY'<br />

8 6 TABLE ACCESS (BY USER ROWID) OF 'ENTITY'<br />

9 5 NESTED LOOPS<br />

10 9 BUFFER (SORT)<br />

11 10 CONNECT BY PUMP<br />

12 9 TABLE ACCESS (FULL) OF 'ENTITY'<br />

13 0 CONNECT BY (WITH FILTERING)<br />

14 13 NESTED LOOPS<br />

15 14 TABLE ACCESS (FULL) OF 'ENTITY'<br />

16 14 TABLE ACCESS (BY USER ROWID) OF 'ENTITY'<br />

17 13 NESTED LOOPS<br />

18 17 BUFFER (SORT)<br />

19 18 CONNECT BY PUMP<br />

20 17 TABLE ACCESS (FULL) OF 'ENTITY'


Statistics<br />

----------------------------------------------------------<br />

4 recursive calls<br />

0 db block gets<br />

208383 consistent gets<br />

0 physical reads<br />

0 redo size<br />

9007 <strong>by</strong>tes sent via SQL*Net to client<br />

591 <strong>by</strong>tes received via SQL*Net from client<br />

10 SQL*Net roundtrips to/from client<br />

27913 sorts (memory)<br />

0 sorts (disk)<br />

122 rows processed<br />

And now using 'WITH'<br />

with alm as (select entity_uuid, count(*) as nbr<br />

from entity_tckt<br />

group <strong>by</strong> entity_uuid)<br />

select e.entity_uuid, sum(nbr) from entity e, alm<br />

where alm.entity_uuid in (select entity_uuid from entity start with entity_uuid<br />

= e.entity_uuid <strong>connect</strong> <strong>by</strong> prior entity_uuid = parent_uuid)<br />

group <strong>by</strong> e.entity_uuid<br />

/<br />

ENTITY_UUID SUM(NBR)<br />

-------------------------------- ----------<br />

026D4A6B547544E08CFEC5DDED3B7777 772<br />

02AD7930E9B94A20A9F4E245B3F4B8C4 1747<br />

04B3AD5F804B4A66AC6C91606EA7019B 74<br />

070547E30C604D4680089959A6DB7684 677<br />

08ABA73521F94F2DB19FF8C1C53ADF06 2<br />

0D65B9BB906B442B9A6BDFEEF3D858AC 86<br />

0F01178489CB421CA5A4C55AEC98300E 4<br />

0FE28D72C40C4D6FBB439A49B0BE6D3F 716<br />

10801AFBCBC44F2F9851E16BC1CCA442 703<br />

13E7CAA5FDEB42518A798A77A19F70B0 15533<br />

1677A02C0B89479BA27F95D7AA3DAC03 9516<br />

ENTITY_UUID SUM(NBR)<br />

-------------------------------- ----------<br />

1AE6BCDA1FA74B0B8DEBEFF13F9A444A 96<br />

1F9BD4CD72FA4879AC338397FB59FD39 74<br />

21FAD03A5EB34D79908E96DA647FF24C 592<br />

235F6C1600584CDD8B3FB1FFCD742E7E 839<br />

23EA0B4618A94C1586BDF08437B97EB0 3067<br />

25CFE1E64F36468DB291CBCF0867B314 59<br />

26C635064E83447B91BA8D125FE74C2A 776<br />

2ABC45D4A5D84684AC29A578C5CCBD3E 1759<br />

2DF37BBB64254DB29081F03D38B5CE33 876<br />

2E1E0646AC9F4BB9A6E4A747B20B2595 7<br />

2FF85FEF82864C0BB75BA4513885ED0E 76<br />

----------------more data ----------------<br />

71 rows selected.<br />

Execution Plan<br />

----------------------------------------------------------<br />

0 SELECT STATEMENT Optimizer=CHOOSE<br />

1 0 SORT (GROUP BY)<br />

2 1 FILTER<br />

3 2 NESTED LOOPS<br />

4 3 VIEW<br />

5 4 SORT (GROUP BY)<br />

6 5 TABLE ACCESS (FULL) OF 'ENTITY_TCKT'<br />

7 3 TABLE ACCESS (FULL) OF 'ENTITY'<br />

8 2 FILTER<br />

9 8 CONNECT BY (WITH FILTERING)<br />

10 9 NESTED LOOPS<br />

11 10 TABLE ACCESS (FULL) OF 'ENTITY'<br />

12 10 TABLE ACCESS (BY USER ROWID) OF 'ENTITY'<br />

13 9 NESTED LOOPS<br />

14 13 BUFFER (SORT)


15 14 CONNECT BY PUMP<br />

16 13 TABLE ACCESS (FULL) OF 'ENTITY'<br />

Statistics<br />

----------------------------------------------------------<br />

0 recursive calls<br />

0 db block gets<br />

197127 consistent gets<br />

0 physical reads<br />

0 redo size<br />

3707 <strong>by</strong>tes sent via SQL*Net to client<br />

547 <strong>by</strong>tes received via SQL*Net from client<br />

6 SQL*Net roundtrips to/from client<br />

27902 sorts (memory)<br />

0 sorts (disk)<br />

71 rows processed<br />

The logical reads will kill us as <strong>the</strong> table grows - Is <strong>the</strong>re ano<strong>the</strong>r way to get <strong>the</strong> logical reads<br />

down. I expect <strong>the</strong> entity table to grow to several hundred thousand records and <strong>the</strong> tickets table<br />

to get very large as well.<br />

Followup March 10, 2006 - 8pm Central time zone:<br />

why would you <strong>connect</strong> <strong>by</strong> <strong>the</strong> entire table????? You would never have a where clause?<br />

single heirarchy with multiple employees March 11, 2006 - 4am Central time zone<br />

Bookmark | Bottom | Top<br />

Reviewer: Rasin from UAE<br />

single heirarchy with multiple employees<br />

Suppose querying <strong>for</strong> empno's 7934, 7369 (actually this will be<br />

determined<br />

<strong>by</strong> <strong>the</strong> input of ano<strong>the</strong>r subquery)<br />

and want to get a single heirarchy instead of multiple heirarchies.<br />

column ename <strong>for</strong>mat a60<br />

select e.empno, lpad(' ', level * 2, ' ') || e.ename ename<br />

from emp e<br />

<strong>connect</strong> <strong>by</strong> e.empno = prior e.mgr<br />

start with empno in (7934, 7369)<br />

EMPNO ENAME<br />

---------- --------------------------------------------------<br />

7369 SMITH<br />

7902 FORD<br />

7566 JONES<br />

7839 KING<br />

7934 MILLER<br />

7782 CLARK<br />

7839 KING<br />

7 rows selected.<br />

currently I am doing a distinct select to remove duplicate but I am<br />

loosing<br />

<strong>the</strong> heirarchy<br />

<strong>by</strong> doing so and also <strong>the</strong> subquery which will return empno's can return<br />

100's of employees,<br />

will this be OK per<strong>for</strong>mance wise ?<br />

select distinct *<br />

from<br />

(<br />

select e.empno, e.ename ename<br />

from emp e<br />

<strong>connect</strong> <strong>by</strong> e.empno = prior e.mgr<br />

start with empno in (7934, 7369)<br />

order siblings <strong>by</strong> empno<br />

)


EMPNO ENAME<br />

---------- --------------------------------------------------<br />

7369 SMITH<br />

7566 JONES<br />

7782 CLARK<br />

7839 KING<br />

7902 FORD<br />

7934 MILLER<br />

My motto is to get <strong>the</strong> employees suppose whose salary<br />

is greater than 3000 along with <strong>the</strong>ir managers up to <strong>the</strong> root of <strong>the</strong> heirarchy.<br />

<strong>Thanks</strong><br />

Followup March 11, 2006 - 3pm Central time zone:<br />

will it be OK per<strong>for</strong>mance wise? You gotta do what you gotta do and if in fact you want to "start<br />

with" hundreds of roots, expand <strong>the</strong>m all of <strong>the</strong> way up and distinct <strong>the</strong>m - that is what you gotta<br />

do.<br />

single heirarchy March 12, 2006 - 3pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Rasin from UAE<br />

It took time to get some hold on heirarchial queries.<br />

My data model looks like following<br />

DEPT 1:M EMP<br />

EMP 1:1 PROJECTS (<strong>the</strong>re can be employees which are not assigned to projects)<br />

PROJECTS 1:M SCHEDULES<br />

create table projects (projno number primary key, pname varchar2(20));<br />

create table schedules (scheduleno number primary key, projno number<br />

constraint proj_fk references projects (projno), schedule_name varchar2(50));<br />

alter table emp add projno number constraint emp_proj_fk<br />

references projects;<br />

insert into projects values (1, 'Engineering');<br />

insert into projects values (2, 'Maintenance');<br />

insert into schedules values (1, 1, 'Schedule1');<br />

insert into schedules values (2, 1, 'Schedule2');<br />

insert into schedules values (3, 2, 'Schedule3');<br />

insert into schedules values (4, 2, 'Schedule4');<br />

--assign project 1 to MILLER<br />

update emp set projno = 1<br />

where empno = 7934;<br />

--assign project 2 to SMITH<br />

update emp set projno = 2<br />

where empno = 7369;<br />

commit;<br />

/*<br />

I want to get employee records along with <strong>the</strong>ir managers<br />

even though <strong>the</strong> managers are not assigned any projects.<br />

The filtering to be done on schedules.schedule_name with in operator<br />

<strong>for</strong> eg., I want to get employees <strong>for</strong> schedule1 and schedule3 along<br />

with <strong>the</strong>ir managers and <strong>the</strong> heirarchy above <strong>the</strong>m<br />

*/<br />

--I tried <strong>the</strong> following query with no rows returned<br />

select e.empno, lpad(' ', level * 2, ' ') || e.ename ename<br />

, dname, s.schedule_name<br />

from emp e, dept d, projects p, schedules s<br />

where<br />

e.deptno = d.deptno<br />

and e.projno = p.projno(+)


and p.projno = s.projno<br />

and s.schedule_name in ('Schedule1', 'Schedule2')<br />

<strong>connect</strong> <strong>by</strong> prior e.empno = e.mgr<br />

start with e.mgr is null<br />

--after watching this and o<strong>the</strong>r threads and reading oracle documentation<br />

-- I came up with <strong>the</strong> following query, I applied your logic of getting<br />

--<strong>the</strong> salary based on heirarchy.<br />

select lpad('*', level * 2, '*') || ename ename, dname, pname<br />

from<br />

(<br />

select e.empno, e.ename ename, e.mgr<br />

, dname, pname,<br />

(select count(p2.projno) from emp e2<br />

,projects p2<br />

where<br />

e2.projno = p2.projno(+)<br />

and<br />

p2.projno in<br />

(select projno from schedules<br />

where schedule_name in ('Schedule1'))<br />

start with e2.empno = e.empno<br />

<strong>connect</strong> <strong>by</strong> prior empno = mgr) proj_cnt<br />

from emp e, dept d, projects p<br />

where<br />

e.deptno = d.deptno<br />

and e.projno = p.projno(+)<br />

and<br />

(<br />

p.projno in<br />

(select projno from schedules<br />

where schedule_name in ('Schedule1'))<br />

or<br />

e.projno is null<br />

)<br />

)<br />

where proj_cnt > 0<br />

start with mgr is null<br />

<strong>connect</strong> <strong>by</strong> prior empno = mgr<br />

/<br />

ENAME DNAME PNAME<br />

------------------------------ -------------- --------------------<br />

**KING ACCOUNTING<br />

****CLARK ACCOUNTING<br />

******MILLER ACCOUNTING Engineering<br />

select lpad('*', level * 2, '*') || ename ename, dname, pname<br />

from<br />

(<br />

select e.empno, e.ename ename, e.mgr<br />

, dname, pname,<br />

(select count(p2.projno) from emp e2<br />

,projects p2<br />

where<br />

e2.projno = p2.projno(+)<br />

and<br />

p2.projno in<br />

(select projno from schedules<br />

where schedule_name in ('Schedule3','Schedule1'))<br />

start with e2.empno = e.empno<br />

<strong>connect</strong> <strong>by</strong> prior empno = mgr) proj_cnt<br />

from emp e, dept d, projects p<br />

where<br />

e.deptno = d.deptno<br />

and e.projno = p.projno(+)<br />

and<br />

(<br />

p.projno in<br />

(select projno from schedules<br />

where schedule_name in ('Schedule3','Schedule1'))<br />

or<br />

e.projno is null<br />

)<br />

)<br />

where proj_cnt > 0<br />

start with mgr is null


<strong>connect</strong> <strong>by</strong> prior empno = mgr<br />

/<br />

ENAME DNAME PNAME<br />

------------------------------ -------------- --------------------<br />

**KING ACCOUNTING<br />

****CLARK ACCOUNTING<br />

******MILLER ACCOUNTING Engineering<br />

****JONES RESEARCH<br />

******FORD RESEARCH<br />

********SMITH RESEARCH Maintenance<br />

--clean up<br />

drop table schedules;<br />

alter table emp drop column projno;<br />

drop table projects;<br />

Won't <strong>connect</strong> <strong>by</strong> entire table March 13, 2006 - 5pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Mike from Dallas, TX<br />

Tom,..<br />

I didn't intend to <strong>connect</strong> <strong>by</strong> <strong>the</strong> entire table, only in this exercise because <strong>the</strong> tickets table<br />

only has records were interested in, <strong>the</strong> ticket table does have a current_lifecycle_state column to<br />

it.<br />

Also, not every entity has tickets. The first query show this where clause. Sorry I didn't have it<br />

in <strong>the</strong> second query.<br />

The task I am trying to accomplish is not to have <strong>the</strong> query go completely down <strong>the</strong> entity table<br />

using <strong>connect</strong> <strong>by</strong> <strong>for</strong> every record resulting from <strong>the</strong> outer <strong>connect</strong> <strong>by</strong>. For Example,..<br />

Level1-<br />

|_<br />

| Level2_<br />

| |_Level3<br />

| |_Level3<br />

| |_Level3<br />

|_Level2_<br />

| |_Level3<br />

| |_Level3_<br />

| |_Level4<br />

| |_Level4<br />

|_Level2_<br />

For each entity, <strong>the</strong>re may/may not be opened tickets in <strong>the</strong> ticket table. One could create a view<br />

on tickets <strong>for</strong> each entity easily using <strong>the</strong> <strong>connect</strong> <strong>by</strong> and scalar subquery that returns <strong>the</strong> number<br />

of open tickets <strong>for</strong> that entity alone.<br />

Would <strong>the</strong>re be a better way to sum up <strong>the</strong>se numbers from <strong>the</strong> bottom up and <strong>the</strong>re<strong>for</strong>e get a faster<br />

return using less resources than having to use <strong>the</strong> <strong>connect</strong> <strong>by</strong> in <strong>the</strong> scalar subquery.<br />

I hope I was a bit clearer.<br />

Mike<br />

Please Help May 6, 2006 - 5pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Isam from Jordan<br />

Hi Tom,<br />

How can I query all Employees ( lets say whose sal >=3000 ) and <strong>the</strong>ir managers.<br />

I used <strong>the</strong> follwing but <strong>the</strong> result is not what I want<br />

select EMPNO,SUBSTR(rpad('*',2*level,'*' ) || ENAME,1,30), MGR,SAL FROM EMP<br />

START WITH MGR IN (SELECT X.EMPNO FROM EMP X WHERE X.SAL>=3000)<br />

CONNECT BY PRIOR EMPNO = MGR<br />

The Result is :


EMPNO SUBSTR(RPAD('*',2*LEVEL,'*')|| MGR SAL<br />

---------- ------------------------------ ---------- ----------<br />

7876 **ADAMS 7788 1100<br />

7566 **JONES 7839 2975<br />

7788 ****SCOTT 7566 3000<br />

7876 ******ADAMS 7788 1100<br />

7902 ****FORD 7566 3000<br />

7369 ******SMITH 7902 800<br />

7698 **BLAKE 7839 2850<br />

7499 ****ALLEN 7698 1600<br />

7521 ****WARD 7698 1250<br />

7654 ****MARTIN 7698 1250<br />

7844 ****TURNER 7698 1500<br />

EMPNO SUBSTR(RPAD('*',2*LEVEL,'*')|| MGR SAL<br />

---------- ------------------------------ ---------- ----------<br />

7900 ****JAMES 7698 950<br />

7782 **CLARK 7839 2450<br />

7934 ****MILLER 7782 1500<br />

7369 **SMITH 7902 800<br />

15 rows selected.<br />

Thank you<br />

Followup May 7, 2006 - 11am Central time zone:<br />

"query all Employees ( lets say whose sal >=3000 ) and <strong>the</strong>ir managers."<br />

why would you use <strong>connect</strong> <strong>by</strong>?<br />

ops$tkyte@ORA10GR2> select ename, mgr, sal from emp where sal >= 3000;<br />

ENAME MGR SAL<br />

---------- ---------- ----------<br />

SCOTT 7566 3000<br />

KING 5000<br />

FORD 7566 3000<br />

or if you needed <strong>the</strong>ir manager name:<br />

1 select a.ename, b.ename mgr, a.sal<br />

2 from emp a, emp b<br />

3 where a.sal >= 3000<br />

4* and a.mgr = b.empno(+)<br />

ops$tkyte@ORA10GR2> /<br />

ENAME MGR SAL<br />

---------- ---------- ----------<br />

FORD JONES 3000<br />

SCOTT JONES 3000<br />

KING 5000<br />

May 7, 2006 - 5pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Isam from Jordan<br />

Thank you <strong>for</strong> reply, <strong>the</strong> idea is to get all employees who satisfies <strong>the</strong> condition (sal>=3000) and<br />

at <strong>the</strong> same time I want to know <strong>the</strong>ir managers of all levels regardless of manager's sal in a tree<br />

structure. <strong>the</strong> result I am looking <strong>for</strong> is like this:<br />

EMPNO name MGR SAL<br />

---------- ------------------------------ ---------- ----------<br />

7839 **KING 5000<br />

7566 ****JONES 7839 2975<br />

7788 ******SCOTT 7566 3000<br />

7902 ******FORD 7566 3000


7902 ******FORD 7566 3000<br />

as you can see , <strong>the</strong> condition returns King, Scott and Ford . but Jones in <strong>the</strong> list because he is<br />

<strong>the</strong> MGR of Scott and Ford.<br />

That what I want. How can I do this.<br />

<strong>Thanks</strong><br />

Followup May 8, 2006 - 7am Central time zone:<br />

that is a bit of a sticky problem. we can do it upside down ra<strong>the</strong>r easily:<br />

ops$tkyte@ORA10GR2> select EMPNO,SUBSTR(rpad('*',2*level,'*' ) || ENAME,1,30), MGR,SAL<br />

2 FROM EMP<br />

3 start with sal >= 3000<br />

4 CONNECT BY PRIOR mgr = empno<br />

5 /<br />

EMPNO SUBSTR(RPAD('*',2*LEVEL,'*')|| MGR SAL<br />

---------- ------------------------------ ---------- ----------<br />

7788 **SCOTT 7566 3000<br />

7566 ****JONES 7839 2975<br />

7839 ******KING 5000<br />

7839 **KING 5000<br />

7902 **FORD 7566 3000<br />

7566 ****JONES 7839 2975<br />

7839 ******KING 5000<br />

7 rows selected.<br />

but going from <strong>the</strong> top down is a tad harder:<br />

ops$tkyte@ORA10GR2> select EMPNO,SUBSTR(rpad('*',2*level,'*' ) || ENAME,1,30), MGR,SAL<br />

2 FROM EMP<br />

3 where empno in ( select EMPNO<br />

4 FROM EMP<br />

5 start with sal >= 3000<br />

6 CONNECT BY PRIOR mgr = empno )<br />

7 start with empno in ( select EMPNO<br />

8 FROM EMP<br />

9 where <strong>connect</strong>_<strong>by</strong>_isleaf = 1<br />

10 start with sal >= 3000<br />

11 <strong>connect</strong> <strong>by</strong> prior mgr = empno )<br />

12 CONNECT BY PRIOR empno = mgr<br />

13 /<br />

EMPNO SUBSTR(RPAD('*',2*LEVEL,'*')|| MGR SAL<br />

---------- ------------------------------ ---------- ----------<br />

7839 **KING 5000<br />

7566 ****JONES 7839 2975<br />

7788 ******SCOTT 7566 3000<br />

7902 ******FORD 7566 3000<br />

it would only make sense to do <strong>the</strong> "start with" subquery IF you have multiple roots and thought<br />

that <strong>the</strong> filtering done <strong>by</strong> <strong>the</strong> subquery would prune away large parts of <strong>the</strong> hierarchy - o<strong>the</strong>rwise,<br />

just build <strong>the</strong> entire hierarchy and <strong>the</strong>n prune it with <strong>the</strong> where clause.<br />

May 9, 2006 - 4pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Isam from Jordan<br />

Thank you <strong>for</strong> help.<br />

That is what I need. I will use this logic in different queries of my project.<br />

<strong>Thanks</strong> again.<br />

Connect <strong>by</strong> doing extra full Scan? June 1, 2006 - 12pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Greg from Toronto


Hi Tom,<br />

Got myself confused (again - *sigh*) and was hoping you could shed some light on something.<br />

I managed to reproduce in this test case:<br />

gregs-ORA10 > drop table junk;<br />

Table dropped.<br />

gregs-ORA10 > create table junk<br />

2 ( col1 number,<br />

3 col2 number )<br />

4 /<br />

Table created.<br />

gregs-ORA10 > insert into junk values ( 1, null );<br />

1 row created.<br />

gregs-ORA10 > insert into junk values ( 2, null );<br />

1 row created.<br />

gregs-ORA10 > insert into junk values ( 3, 2 );<br />

1 row created.<br />

gregs-ORA10 > insert into junk values ( 4, 3 );<br />

1 row created.<br />

gregs-ORA10 > insert into junk values ( 5, null );<br />

1 row created.<br />

gregs-ORA10 > insert into junk values ( 6, 5 );<br />

1 row created.<br />

gregs-ORA10 > insert into junk values ( 7, 6 );<br />

1 row created.<br />

gregs-ORA10 > insert into junk values ( 8, null );<br />

1 row created.<br />

gregs-ORA10 > commit;<br />

Commit complete.<br />

gregs-ORA10 > set autotrace traceonly explain<br />

gregs-ORA10 > select col1<br />

2 from junk<br />

3 start with col2 = 2<br />

4 <strong>connect</strong> <strong>by</strong> prior col1 = col2<br />

5 /<br />

Execution Plan<br />

----------------------------------------------------------<br />

Plan hash value: 3214713417<br />

-------------------------------------------------------------------------------------<br />

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |<br />

-------------------------------------------------------------------------------------<br />

| 0 | SELECT STATEMENT | | 8 | 208 | 2 (0)| 00:00:01 |<br />

|* 1 | CONNECT BY WITHOUT FILTERING| | | | | |<br />

|* 2 | TABLE ACCESS FULL | JUNK | 1 | 52 | 2 (0)| 00:00:01 |<br />

| 3 | TABLE ACCESS FULL | JUNK | 8 | 208 | 2 (0)| 00:00:01 |<br />

-------------------------------------------------------------------------------------<br />

Predicate In<strong>for</strong>mation (identified <strong>by</strong> operation id):<br />

---------------------------------------------------<br />

1 - access("COL2"=PRIOR "COL1")


2 - filter("COL2"=2)<br />

Note<br />

-----<br />

- dynamic sampling used <strong>for</strong> this statement<br />

gregs-ORA10 > create index junk_ind1 on junk ( col2 )<br />

2 /<br />

Index created.<br />

gregs-ORA10 > select col1<br />

2 from junk<br />

3 start with col2 = 2<br />

4 <strong>connect</strong> <strong>by</strong> prior col1 = col2<br />

5 /<br />

Execution Plan<br />

----------------------------------------------------------<br />

Plan hash value: 3206560888<br />

-------------------------------------------------------------------------------------------<br />

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |<br />

-------------------------------------------------------------------------------------------<br />

| 0 | SELECT STATEMENT | | 1 | 26 | 1 (0)| 00:00:01 |<br />

|* 1 | CONNECT BY WITH FILTERING | | | | | |<br />

| 2 | TABLE ACCESS BY INDEX ROWID | JUNK | 1 | 52 | 1 (0)| 00:00:01 |<br />

|* 3 | INDEX RANGE SCAN | JUNK_IND1 | 1 | | 1 (0)| 00:00:01 |<br />

| 4 | NESTED LOOPS | | | | | |<br />

| 5 | BUFFER SORT | | | | | |<br />

| 6 | CONNECT BY PUMP | | | | | |<br />

| 7 | TABLE ACCESS BY INDEX ROWID| JUNK | 1 | 26 | 1 (0)| 00:00:01 |<br />

|* 8 | INDEX RANGE SCAN | JUNK_IND1 | 1 | | 1 (0)| 00:00:01 |<br />

| 9 | TABLE ACCESS FULL | JUNK | 8 | 312 | 2 (0)| 00:00:01 |<br />

-------------------------------------------------------------------------------------------<br />

Predicate In<strong>for</strong>mation (identified <strong>by</strong> operation id):<br />

---------------------------------------------------<br />

1 - access("COL2"=PRIOR "COL1")<br />

3 - access("COL2"=2)<br />

8 - access("COL2"=PRIOR "COL1")<br />

Note<br />

-----<br />

- dynamic sampling used <strong>for</strong> this statement<br />

gregs-ORA10 > analyze table junk compute statistics;<br />

Table analyzed.<br />

gregs-ORA10 > select col1<br />

2 from junk<br />

3 start with col2 = 2<br />

4 <strong>connect</strong> <strong>by</strong> prior col1 = col2<br />

5 /<br />

Execution Plan<br />

----------------------------------------------------------<br />

Plan hash value: 3206560888<br />

-------------------------------------------------------------------------------------------<br />

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |<br />

-------------------------------------------------------------------------------------------<br />

| 0 | SELECT STATEMENT | | 2 | 6 | 1 (0)| 00:00:01 |<br />

|* 1 | CONNECT BY WITH FILTERING | | | | | |<br />

| 2 | TABLE ACCESS BY INDEX ROWID | JUNK | 1 | 6 | 1 (0)| 00:00:01 |<br />

|* 3 | INDEX RANGE SCAN | JUNK_IND1 | 1 | | 1 (0)| 00:00:01 |<br />

| 4 | NESTED LOOPS | | | | | |<br />

| 5 | BUFFER SORT | | | | | |<br />

| 6 | CONNECT BY PUMP | | | | | |<br />

| 7 | TABLE ACCESS BY INDEX ROWID| JUNK | 2 | 6 | 1 (0)| 00:00:01 |<br />

|* 8 | INDEX RANGE SCAN | JUNK_IND1 | 1 | | 1 (0)| 00:00:01 |<br />

| 9 | TABLE ACCESS FULL | JUNK | 8 | 40 | 2 (0)| 00:00:01 |<br />

-------------------------------------------------------------------------------------------<br />

Predicate In<strong>for</strong>mation (identified <strong>by</strong> operation id):<br />

---------------------------------------------------


1 - access("COL2"=PRIOR "COL1")<br />

3 - access("COL2"=2)<br />

8 - access("COL2"=PRIOR "COL1")<br />

This is and Oracle 10g (10.2.0.2.0) database, and my <strong>question</strong> is, why is Oracle doing that final<br />

FULL SCAN on JUNK ?? I just don't understand <strong>the</strong> logic - I don't know where it's coming from. I<br />

think <strong>the</strong> "START WITH" results in one Index scan, and <strong>the</strong> <strong>connect</strong> <strong>by</strong> results in <strong>the</strong> 2nd ... but I<br />

can't figure out that 3rd scan.<br />

The original query ran fine in 8i (doing 2 range scans, and no third scan at all!)<br />

Sorry ... don't mean to harass .. ;) June 5, 2006 - 10am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Greg from Toronto<br />

I know you're busy, and I know you don't always see every post ... however, we're really stuck on<br />

this, I've tried <strong>the</strong> Oracle Metalink Forums ... nothing yet ... and we've continued to try a few<br />

o<strong>the</strong>r things .. (I'm currently reading through Jonathan's book "Cost Based Oracle Fundamentals" in<br />

<strong>the</strong> hopes of sheding some light on it .. but I'm still stumped ...<br />

If you can find some time in your busy schedule to take a peek at this one, I'd be internally<br />

grateful ... if you can't ... well .. I understand!! ;)<br />

Followup June 5, 2006 - 10am Central time zone:<br />

this is a teeny tiny table, I don't see <strong>the</strong> point - <strong>the</strong> table is too small to use <strong>the</strong> index here.<br />

True .. however ... June 6, 2006 - 1pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Greg from Toronto<br />

This is a re-created test case based on a real problem I have with a table that's 17 million rows<br />

...<br />

Same explain is showing up in that query as this one.<br />

Statistics are all up to date ...<br />

I can also re-produce <strong>the</strong> test case with a ~17 million row table .. but I was hoping an 8 row table<br />

might be easier to work with ??<br />

Just to be clear .. it's not that I'm going to complain about <strong>the</strong> full table scan .. (yet) .. just<br />

wondering why Oracle has decided to make that third pass ... I can't seem to understand why it<br />

would want to make a third pass on that data ... all because we added an index ??<br />

(Note <strong>the</strong> explain with no index does 2x full scans ... but after <strong>the</strong> index is added, we're making<br />

3x passes ... 2x index scans, + 1x full ... )<br />

I'm only trying to understand that third pass ..<br />

<strong>Thanks</strong>!<br />

Followup June 6, 2006 - 2pm Central time zone:<br />

it is a "dummy no-op" pass, it is an artifact of <strong>the</strong> explain plan output.<br />

select col1<br />

from junk<br />

start with col2 = 2<br />

<strong>connect</strong> <strong>by</strong> prior col1 = col2<br />

call count cpu elapsed disk query current rows<br />

------- ------ -------- ---------- ---------- ---------- ---------- ----------


------- ------ -------- ---------- ---------- ---------- ---------- ----------<br />

Parse 1 0.00 0.00 0 0 0 0<br />

Execute 1 0.00 0.00 0 0 0 0<br />

Fetch 2 0.00 0.00 0 5 0 2<br />

------- ------ -------- ---------- ---------- ---------- ---------- ---------total<br />

4 0.00 0.00 0 5 0 2<br />

Misses in library cache during parse: 1<br />

Optimizer mode: ALL_ROWS<br />

Parsing user id: 140<br />

Rows Row Source Operation<br />

------- ---------------------------------------------------<br />

2 CONNECT BY WITH FILTERING (cr=5 pr=0 pw=0 time=239 us)<br />

1 TABLE ACCESS BY INDEX ROWID JUNK (cr=2 pr=0 pw=0 time=61 us)<br />

1 INDEX RANGE SCAN JUNK_IND1 (cr=1 pr=0 pw=0 time=40 us)(object id<br />

1 NESTED LOOPS (cr=3 pr=0 pw=0 time=113 us)<br />

2 BUFFER SORT (cr=0 pr=0 pw=0 time=65 us)<br />

2 CONNECT BY PUMP (cr=0 pr=0 pw=0 time=29 us)<br />

1 TABLE ACCESS BY INDEX ROWID JUNK (cr=3 pr=0 pw=0 time=37 us)<br />

1 INDEX RANGE SCAN JUNK_IND1 (cr=2 pr=0 pw=0 time=24 us)(object id<br />

0 TABLE ACCESS FULL JUNK (cr=0 pr=0 pw=0 time=0 us)<br />

see <strong>the</strong> zero rows, cr=0, no activity - do you have a tkprof where <strong>the</strong> row source operation shows<br />

"not zero"<br />

yes ... but ... June 6, 2006 - 8pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Greg from Toronto<br />

Yeah .. but I'm not sure we're looking at <strong>the</strong> same thing! ;)<br />

In my original post, <strong>the</strong> set autotrace traceonly explain command spit out an explain <strong>for</strong>matted a<br />

bit different than yours (which is why I'm thinking I'm not looking at <strong>the</strong> right thing .. hehe) ...<br />

and it shows 8 rows on this one:<br />

| 9 | TABLE ACCESS FULL | JUNK | 8 | 40 | 2 (0)| 00:00:01 |<br />

I can try to re-run with:<br />

set autotrace traceonly statistics ??<br />

Followup June 6, 2006 - 9pm Central time zone:<br />

that is an autotrace, it is not "what happened".<br />

tkprof shows "what happened"<br />

Here is more in<strong>for</strong>mation:<br />

...<br />

The <strong>connect</strong> <strong>by</strong> row source uses a sort to store <strong>the</strong> rows it will be working on. If <strong>the</strong> filtering<br />

option is used, <strong>connect</strong> <strong>by</strong> needs <strong>the</strong> sort to detect duplicates as <strong>the</strong> rows are inserted into <strong>the</strong><br />

sort. If <strong>the</strong> sort spills to disk, it can no longer detect duplicates at <strong>the</strong> time <strong>the</strong> rows are<br />

inserted (<strong>the</strong> duplicates will be detected later, when <strong>the</strong> sort runs are merged). There<strong>for</strong>e if <strong>the</strong><br />

sort spills to disk, <strong>the</strong> <strong>connect</strong> <strong>by</strong> will switch to "no filtering" mode. The extra line in <strong>the</strong> plan<br />

is <strong>the</strong> row source that will be used if <strong>the</strong> switch to "no filtering" happens.<br />

.............<br />

Tree Walking from <strong>the</strong> result of a join June 14, 2006 - 2pm Central time zone<br />

Bookmark | Bottom | Top<br />

Reviewer: Mike Jones from England<br />

Help! I'm trying to write a Query where given a result set, one of <strong>the</strong> columns is an index into a<br />

hierarchy. I want to report <strong>the</strong> result set and <strong>the</strong>n each of <strong>the</strong> corresponding parents back up <strong>the</strong><br />

hierachy, sort of like reporting some in<strong>for</strong>mation about a person and <strong>the</strong>n each of <strong>the</strong>ir dad, and<br />

<strong>the</strong>n that person and <strong>the</strong>ir dads dad etc. all <strong>the</strong> way back up.


I can't seem to get it working, I can't get <strong>the</strong> filtering to <strong>the</strong> correct tree-walk right.<br />

Hopefully <strong>the</strong> below illistrates this. I should only get 6 rows back, <strong>the</strong> 2 base rows from <strong>the</strong> ilv<br />

and <strong>the</strong>se reported 3 times each <strong>for</strong> <strong>the</strong> 3 levels of <strong>the</strong> hierachy. In practice <strong>the</strong> ILV rows would<br />

report different IDX values and so <strong>the</strong> relative tree walks would differ. Can you help point out<br />

where I'm going mad?<br />

<strong>Thanks</strong>,<br />

Mike.<br />

create table hierarchy_table as<br />

select object_name, rownum idx, decode(rownum -1,0,null,rownum-1) parent_idx<br />

from user_objects<br />

where rownum < 4<br />

/<br />

create table base_x as<br />

select 3 idx, object_name<br />

from user_objects<br />

where rownum < 3<br />

/<br />

create table base_y as<br />

select *<br />

from base_x<br />

/<br />

select ilv.object_name, ilv.idx ilv_idx, ht.object_name ht_name, level<br />

from<br />

( select x.object_name, x.idx<br />

from base_x x, base_y y<br />

where x.idx = y.idx<br />

) ilv,<br />

hierarchy_table ht<br />

<strong>connect</strong> <strong>by</strong> prior ht.parent_idx = ht.idx<br />

start with ilv.idx = ht.idx<br />

order <strong>by</strong> 1<br />

/<br />

Followup June 15, 2006 - 8am Central time zone:<br />

I'm not sure why this isn't just a normal <strong>connect</strong> <strong>by</strong> - your text description is that of a <strong>connect</strong><br />

<strong>by</strong>:<br />

...<br />

I want to report <strong>the</strong> result set and <strong>the</strong>n each of<br />

<strong>the</strong> corresponding parents back up <strong>the</strong> hierachy,<br />

.....<br />

(<strong>the</strong> result set = START WITH rows, corresponding parents = CONNNECT BY rows)<br />

so, maybe if you show us what you get and what you think you should get I can understand better.<br />

Connect <strong>by</strong> Join Bug? August 9, 2006 - 10am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Matt Turner from AA<br />

Tom I have produced a sample TEST case, can you tell me if you think this is working as <strong>the</strong> manual<br />

or is a bug with <strong>the</strong> join and <strong>connect</strong> <strong>by</strong>:<br />

We have a user_state table which stores a script number <strong>for</strong> a <strong>connect</strong>ed user(simplified):<br />

CREATE TABLE user_state (username VARCHAR2(30), scriptid NUMBER );<br />

Both Fred and Bill are running script 1.<br />

INSERT INTO user_state VALUES('FRED',1);<br />

INSERT INTO user_state VALUES('BILL',1);<br />

We have a second table - script structure, which has <strong>the</strong> script id, and a list of <strong>question</strong>s (TREE).


INSERT INTO script_structure VALUES (1,1,NULL);<br />

INSERT INTO script_structure VALUES (1,2,1);<br />

INSERT INTO script_structure VALUES (1,3,2);<br />

INSERT INTO script_structure VALUES (1,4,2);<br />

So, <strong>for</strong> Script 1:<br />

SELECT *<br />

FROM script_structure<br />

WHERE scriptid = 1<br />

START WITH parent_que_id IS NULL<br />

CONNECT BY PRIOR que_id = parent_que_id<br />

AND PRIOR scriptid = scriptid;<br />

1 1<br />

1 2 1<br />

1 3 2<br />

1 4 2<br />

Looks ok so far. Now I want to join to <strong>the</strong> user_state table.<br />

SELECT *<br />

FROM script_structure ss,<br />

user_state us<br />

WHERE username = 'FRED' /* Derived from a DB context */<br />

AND us.scriptid = ss.scriptid<br />

START WITH parent_que_id IS NULL<br />

CONNECT BY PRIOR que_id = parent_que_id;<br />

This is where it goes pear shaped with <strong>the</strong> result set:<br />

1 1 FRED 1<br />

1 2 1 FRED 1<br />

1 3 2 FRED 1<br />

1 4 2 FRED 1<br />

1 3 2 FRED 1<br />

1 4 2 FRED 1<br />

1 2 1 FRED 1<br />

1 3 2 FRED 1<br />

1 4 2 FRED 1<br />

1 3 2 FRED 1<br />

1 4 2 FRED 1<br />

I do have a work around, that involves using an inline view to restrict <strong>the</strong> dataset prior to <strong>the</strong><br />

tree walk.<br />

SELECT que_id, parent_que_id<br />

FROM<br />

(<br />

SELECT *<br />

FROM script_structure ss,<br />

user_state us<br />

WHERE username = 'FRED'<br />

AND us.scriptid = ss.scriptid )<br />

START WITH parent_que_id IS NULL<br />

CONNECT BY PRIOR que_id = parent_que_id;<br />

1<br />

2 1<br />

3 2<br />

4 2<br />

So it seems that <strong>the</strong> username filter predicate isn't being applied at <strong>the</strong> time of <strong>the</strong> join.<br />

Is this a bona-fide bug?


Followup August 9, 2006 - 11am Central time zone:<br />

with <strong>the</strong> <strong>connect</strong> <strong>by</strong> and joins:<br />

join is done<br />

start with applied<br />

<strong>connect</strong> <strong>by</strong> done<br />

where clause applied to results<br />

username = 'FRED' is a predicate, applied after <strong>the</strong> hierarchy is built.<br />

time zone<br />

Tom Can you please tell me <strong>the</strong> workaround <strong>for</strong> Connect By Loop Error August 14, 2006 - 8am Central<br />

Reviewer: Nitin Joshi from INDIA<br />

Hi Tom,<br />

Bookmark | Bottom | Top<br />

Can you please tell is <strong>the</strong>re any workaround to avoid <strong>connect</strong> By loop like 'NOCYCLE' <strong>the</strong> one which<br />

we have in 10g<br />

Regards<br />

Nitin S. Jsohi<br />

Followup August 14, 2006 - 11am Central time zone:<br />

only if you can encode it in <strong>the</strong> query (eg: describe to me what condition would always exist <strong>for</strong><br />

you to have a <strong>connect</strong> <strong>by</strong>)<br />

<strong>the</strong>n we can possibly filter it in <strong>the</strong> <strong>connect</strong> <strong>by</strong> clause (eg: if <strong>the</strong>re is some "rule")<br />

<strong>for</strong> example, if you hit <strong>the</strong> same ID as you started with, you should "stop" (eg: all of your data is<br />

a "circle")<br />

ops$tkyte%ORA9IR2> create table t ( id number, pid number );<br />

Table created.<br />

ops$tkyte%ORA9IR2><br />

ops$tkyte%ORA9IR2> insert into t values ( 1, 5 );<br />

1 row created.<br />

ops$tkyte%ORA9IR2> insert into t values ( 2, 1 );<br />

1 row created.<br />

ops$tkyte%ORA9IR2> insert into t values ( 3, 2 );<br />

1 row created.<br />

ops$tkyte%ORA9IR2> insert into t values ( 4, 3 );<br />

1 row created.<br />

ops$tkyte%ORA9IR2> insert into t values ( 5, 4 );<br />

1 row created.<br />

ops$tkyte%ORA9IR2><br />

ops$tkyte%ORA9IR2> select *<br />

2 from t<br />

3 start with id = 1<br />

4 <strong>connect</strong> <strong>by</strong> prior id = pid;<br />

ERROR:<br />

ORA-01436: CONNECT BY loop in user data


no rows selected<br />

ops$tkyte%ORA9IR2><br />

ops$tkyte%ORA9IR2> select *<br />

2 from t<br />

3 start with id = 1<br />

4 <strong>connect</strong> <strong>by</strong> prior id = pid and id 1;<br />

ID PID<br />

---------- ----------<br />

1 5<br />

2 1<br />

3 2<br />

4 3<br />

5 4<br />

but short of having some "logic" or "rule" that can be used to describe when you would have a<br />

loop....<br />

Connect By August 26, 2006 - 5pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Saeed from jordan<br />

Hi Tom ,<br />

I have <strong>the</strong> following table and its data;<br />

CREATE TABLE MyTable<br />

(MyPK NUMBER(3)NOT NULL,<br />

COL VARCHAR2(20)NOT NULL,<br />

STATUS NUMBER(1) DEFAULT 1 NOT NULL,<br />

PARENT_ID NUMBER(3)<br />

);<br />

*** Status has one of two values (1=Enabled,0=Disabled)<br />

ALTER TABLE Mytable ADD (<br />

CONSTRAINT StatusCons CHECK (STATUS IN (0, 1)));<br />

ALTER TABLE Mytable ADD (<br />

CONSTRAINT MyTable_PK PRIMARY KEY (MyPK));<br />

ALTER TABLE Mytable ADD (<br />

CONSTRAINT MyTable_MyTable_FK FOREIGN KEY (PARENT_ID)<br />

REFERENCES Mytable (MyPK));<br />

INSERT INTO Mytable ( MyPK, col, STATUS, PARENT_ID ) VALUES (<br />

0, '0_', 1, NULL);<br />

INSERT INTO Mytable ( MyPK, col, STATUS, PARENT_ID ) VALUES (<br />

1, '1_0', 1, 0);<br />

INSERT INTO Mytable ( MyPK, col, STATUS, PARENT_ID ) VALUES (<br />

2, '2_0', 1, 0);<br />

INSERT INTO Mytable ( MyPK, col, STATUS, PARENT_ID ) VALUES (<br />

3, '3_1', 1, 1);<br />

INSERT INTO Mytable ( MyPK, col, STATUS, PARENT_ID ) VALUES (<br />

4, '4_1', 1, 1);<br />

INSERT INTO Mytable ( MyPK, col, STATUS, PARENT_ID ) VALUES (<br />

5, '5_2', 1, 2);<br />

INSERT INTO Mytable ( MyPK, col, STATUS, PARENT_ID ) VALUES (<br />

6, '6_2', 1, 2);<br />

INSERT INTO Mytable ( MyPK, col, STATUS, PARENT_ID ) VALUES (<br />

7, '7_0', 1, 0);<br />

INSERT INTO Mytable ( MyPK, col, STATUS, PARENT_ID ) VALUES (<br />

8, '8_0', 0, 0);<br />

INSERT INTO Mytable ( MyPK, col, STATUS, PARENT_ID ) VALUES (<br />

9, '9_0', 0, 0);<br />

INSERT INTO Mytable ( MyPK, col, STATUS, PARENT_ID ) VALUES (<br />

10, '10_7', 1, 7);<br />

INSERT INTO Mytable ( MyPK, col, STATUS, PARENT_ID ) VALUES (<br />

11, '11_7', 1, 7);<br />

INSERT INTO Mytable ( MyPK, col, STATUS, PARENT_ID ) VALUES (<br />

12, '12_7', 1, 7);<br />

INSERT INTO Mytable ( MyPK, col, STATUS, PARENT_ID ) VALUES (<br />

13, '13_12', 0, 12);<br />

INSERT INTO Mytable ( MyPK, col, STATUS, PARENT_ID ) VALUES (<br />

14, '14_12', 0, 12);<br />

INSERT INTO Mytable ( MyPK, col, STATUS, PARENT_ID ) VALUES (


15, '15_11', 0, 11);<br />

INSERT INTO Mytable ( MyPK, col, STATUS, PARENT_ID ) VALUES (<br />

16, '16_15', 1, 15);<br />

COMMIT;<br />

MY QUESTION IS :<br />

How can I get rows that satisfy <strong>the</strong> following conditions :<br />

All children with STATUS=1 AND <strong>the</strong>ir parents have STATUS=1.<br />

For Parents with status=0 , don't get <strong>the</strong>ir children (regardless of Children's STATUS).<br />

<strong>for</strong> Children with Status=0 , don't get <strong>the</strong>m.<br />

<strong>Thanks</strong> <strong>for</strong> help<br />

Saeed<br />

Followup August 27, 2006 - 9pm Central time zone:<br />

do you just want "root" parents - or all parents (eg: what is <strong>the</strong> start with here)<br />

August 29, 2006 - 6am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Saeed from Jordan<br />

Hi Tom ,<br />

What I want is (all Parents having STATUS=1 and <strong>the</strong>ir children having STATUS=1 ) in a tree<br />

structure.<br />

i.e All Rows selected must have STATUS=1.<br />

(DON'T INCLUDE ANY CHILD HIS PARENT'S STATUS=0.)<br />

I know my English Language is not good enough , but I hope you got <strong>the</strong> idea.<br />

<strong>Thanks</strong> again<br />

Saeed<br />

Followup August 29, 2006 - 7am Central time zone:<br />

just add "and status = 1" to <strong>the</strong> <strong>connect</strong> <strong>by</strong> clause <strong>the</strong>n, if status 1, it will stop tra<strong>versi</strong>ng<br />

<strong>the</strong> tree.<br />

August 29, 2006 - 4pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Saeed from Jordan<br />

<strong>Thanks</strong> TOM ,<br />

It's worked well<br />

Multiple records within a hierarchy. October 10, 2006 - 11am Central time zone<br />

Bookmark | Bottom | Top<br />

Reviewer: A reader<br />

Is it possible to get multiple records of a particular employee within a hierarchy in specific<br />

cases?<br />

I have <strong>the</strong> following table structure.<br />

create table EMPINFO<br />

( c_STATUS VARCHAR2(2),<br />

c_GROUP VARCHAR2(10),<br />

c_SYSID VARCHAR2(10),<br />

c_SYSTEM VARCHAR2(10),<br />

c_NAME VARCHAR2(40),


c_NAME VARCHAR2(40),<br />

c_ID VARCHAR2(10),<br />

c_MGRID VARCHAR2(10),<br />

c_MGRNAME VARCHAR2(40)<br />

);<br />

Typical Data is<br />

insert into EMPINFO VALUES ('A','HR','','','JASON BICKER','0000001','','');<br />

insert into EMPINFO VALUES ('A','HR','','','NANCY WALTER','0400001','0000001','JASON BICKER');<br />

insert into EMPINFO VALUES ('','MF','NAN3005','WIN','NANCY WALTER','0400001','','');<br />

insert into EMPINFO VALUES ('','MF','NAN3005','UNIX','NANCY WALTER','0400001','','');<br />

insert into EMPINFO VALUES ('A','HR','','','ADAM BRYAN','0400002','0000001','JASON BICKER');<br />

insert into EMPINFO VALUES ('','MF','BRY0034','WIN','ADAM BRYAN','0400002','','');<br />

insert into EMPINFO VALUES ('A','HR','','','STING CORY','0400003','0400002','ADAM BRYAN');<br />

insert into EMPINFO VALUES ('','MF','STI4040','UNIX','STING CORY','0400003','','');<br />

For example I want to display all records of NANCY WALTER. The c_MGRID is blank <strong>for</strong> 2nd and 3rd<br />

record of NANCY WALTER. Similar structure will follow <strong>for</strong> o<strong>the</strong>r records as well.<br />

Regards,<br />

Rao<br />

Followup October 10, 2006 - 8pm Central time zone:<br />

if nancy walker doesn't have a manager, how is "she" in <strong>the</strong> hierarchy????<br />

October 11, 2006 - 9am Central time zone Bookmark | Bottom | Top<br />

Reviewer: A reader<br />

This table is kind of a master table that includes employee and its manager and <strong>the</strong><br />

systems(Unix/Windows) and userids <strong>the</strong> employee has on it.<br />

Nancy Walker has a manager whose c_MGRID is populated <strong>for</strong> <strong>the</strong> 1st row. But <strong>the</strong> 2nd and 3rd row of<br />

Nancy Walker shows<br />

<strong>the</strong> system and <strong>the</strong> userid's she has access to. c_MGRID of this 2nd and 3rd row is empty. Is it<br />

possible to include <strong>the</strong>se rows in <strong>the</strong> hierarchy as well similar to "start with .... <strong>connect</strong> <strong>by</strong><br />

prior"<br />

Followup October 11, 2006 - 3pm Central time zone:<br />

need more details, can <strong>the</strong>re be two (or zero, or more than two) records <strong>for</strong> nancy in <strong>the</strong>re with<br />

c_mgrid filled in? would <strong>the</strong>y have <strong>the</strong> same value?<br />

October 11, 2006 - 4pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: A reader<br />

For a user<br />

a) only one record wherver c_mgrid is filled. This will always be <strong>the</strong>re when he/she joins <strong>the</strong><br />

company.<br />

b) 0 or more records wherever c_mgrid is not filled. Rows will be <strong>the</strong>re only if user has access to<br />

any Windows/Unix/Mainframe systems and has an userid on it.<br />

Followup October 11, 2006 - 8pm Central time zone:<br />

use this as your "source" to <strong>connect</strong> <strong>by</strong> on:<br />

ops$tkyte%ORA10GR2> select c_name, max( c_mgrid) over (partition <strong>by</strong> c_name) mgrid<br />

2 from empinfo;<br />

C_NAME MGRID<br />

---------------------------------------- ----------<br />

ADAM BRYAN 0000001<br />

ADAM BRYAN 0000001


ADAM BRYAN 0000001<br />

JASON BICKER<br />

NANCY WALTER 0000001<br />

NANCY WALTER 0000001<br />

NANCY WALTER 0000001<br />

STING CORY 0400002<br />

STING CORY 0400002<br />

8 rows selected.<br />

October 12, 2006 - 11am Central time zone Bookmark | Bottom | Top<br />

Reviewer: A reader<br />

I am sorry I did not get you. The order does not seem right.<br />

select c_name, max( c_mgrid) over (partition <strong>by</strong> c_name) mgrid from empinfo<br />

start with c_id='0000001'<br />

<strong>connect</strong> <strong>by</strong> prior c_id=c_mgrid<br />

The O/p is<br />

C_NAME MGRID<br />

------------------------------<br />

ADAM BRYAN 0000001<br />

JASON BICKER<br />

NANCY WALTER 0000001<br />

STING CORY 0400002<br />

The required O/p is<br />

C_NAME MGRID<br />

------------------------------<br />

JASON BICKER<br />

NANCY WALTER '0000001'<br />

NANCY WALTER '0000001' ->> Access to Windows System<br />

NANCY WALTER '0000001' ->> Access to unix System<br />

ADAM BRYAN '0000001'<br />

ADAM BRYAN '0000001' ->> Access to Windows<br />

STING CORY '0400002'<br />

STING CORY '0400002' ->> Access to unix<br />

Followup October 12, 2006 - 11am Central time zone:<br />

no, use my query as your "table<br />

select ..<br />

from (MY_QUERY)<br />

start with<br />

<strong>connect</strong> <strong>by</strong><br />

October 12, 2006 - 11am Central time zone Bookmark | Bottom | Top<br />

Reviewer: A reader<br />

The ' ' in <strong>the</strong> C_MGRID section of Required O/p is unintentional. It should be<br />

The required O/p is<br />

C_NAME MGRID<br />

------------------------------<br />

JASON BICKER<br />

NANCY WALTER 0000001<br />

NANCY WALTER 0000001 ->> Access to Windows System<br />

NANCY WALTER 0000001 ->> Access to unix System<br />

ADAM BRYAN 0000001<br />

ADAM BRYAN 0000001 ->> Access to Windows<br />

STING CORY 0400002<br />

STING CORY 0400002 ->> Access to unix<br />

October 12, 2006 - 3pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: A reader


<strong>Thanks</strong> a lot <strong>for</strong> <strong>the</strong> in<strong>for</strong>mation.<br />

CONNECT BY October 27, 2006 - 1pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: lore from Mexico<br />

The ansewer is too clear and has an example that clarify <strong>the</strong> use of <strong>the</strong> <strong>connect</strong> <strong>by</strong><br />

Connect <strong>by</strong> February 15, 2007 - 5pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: guest from India<br />

Hi Tom,<br />

I need output like given below<br />

Key2 Key1 ENAME<br />

---------- ------------------------------<br />

1 1 **FRENCH<br />

1 2 ****BLAKE<br />

1 3 ******ADAMS<br />

2 1 **FRENCH<br />

2 2 ****DAVIS<br />

2 3 ******ADAMS<br />

3 1 **FRENCH<br />

3 2 ****OLIVER<br />

4 1 **BALMAR<br />

4 2 ****GOWD<br />

Could you please give me a query to get <strong>the</strong> above result. with key1 and key2 and tree should have full start to stop.<br />

eg:<br />

A->B->D<br />

A->C->E<br />

F->G->H<br />

I->J<br />

I->K<br />

Database is 10g.<br />

<strong>Thanks</strong>,<br />

Followup February 16, 2007 - 1pm Central time zone:<br />

hmmm<br />

eh?<br />

geez.....<br />

<strong>connect</strong> <strong>by</strong> February 16, 2007 - 2pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Guest from UK<br />

Hi Tom,<br />

I have written <strong>the</strong> below code to achieve a hierarchy tree. It works fine but takes lot of time and memory. sometimes sorting<br />

fails.<br />

Could you please tell me how can i reduce <strong>the</strong> run time <strong>for</strong> <strong>the</strong> below proc. None of <strong>the</strong> table has indexes. it has to process<br />

millions of records to give <strong>the</strong> tree with parent child relationship.<br />

Example Input:<br />

insert into po(po_NUM,mat_code,op_plant, sc, batch_num ) values (1, 'C','GBP','DW','A');<br />

insert into po(po_NUM,mat_code,op_plant, sc, batch_num ) values (2, 'C','GBP','DW','C');<br />

insert into po(po_NUM,mat_code,op_plant, sc, batch_num ) values (3, 'C','GBP','DW','G');<br />

insert into po(po_NUM,mat_code,op_plant, sc, batch_num ) values (4, 'C','GBP','DW','F');<br />

insert into po(po_NUM,mat_code,op_plant, sc, batch_num ) values (5, 'C','GBP','DW','H');<br />

insert into po(po_NUM,mat_code,op_plant, sc, batch_num ) values (6, 'C','GBP','DW','I');<br />

insert into po(po_NUM,mat_code,op_plant, sc, batch_num ) values (7, 'C','GBP','DW','J');<br />

insert into po(po_NUM,mat_code,op_plant, sc, batch_num ) values (8, 'C','GBP','DW','L');<br />

insert into po(po_NUM,mat_code,op_plant, sc, batch_num ) values (9, 'C','GBP','DW','K');


insert into po(po_NUM,mat_code,op_plant, sc, batch_num ) values (9, 'C','GBP','DW','K');<br />

insert into po(po_NUM,mat_code,op_plant, sc, batch_num ) values (10, 'C','GBP','DW','P');<br />

insert into po(po_NUM,mat_code,op_plant, sc, batch_num ) values (11, 'C','GBP','DW','D');<br />

insert into po(po_NUM,mat_code,op_plant, sc, batch_num ) values (12, 'C','GBP','DW','M');<br />

insert into po(po_NUM,mat_code,op_plant, sc, batch_num ) values (13, 'C','GBP','DW','O');<br />

insert into po(po_NUM,mat_code,op_plant, sc, batch_num ) values (14, 'C','GBP','DW','Q');<br />

insert into po(po_NUM,mat_code,op_plant, sc, batch_num ) values (15, 'C','GBP','DW','N');<br />

insert into po(po_NUM,mat_code,op_plant, sc, batch_num ) values (16, 'C','GBP','DW','E');<br />

insert into po(po_NUM,mat_code,op_plant, sc, batch_num ) values (17, 'C','GBP','DW','B');<br />

insert into pod(po_NUM,op_plant, sc, i_mat_code, ib, ip_plant) values (1, 'GBP','DW', 'C','C', 'GBP');<br />

insert into pod(po_NUM,op_plant, sc, i_mat_code, ib, ip_plant) values (1, 'GBP','DW', 'C','D', 'GBP');<br />

insert into pod(po_NUM,op_plant, sc, i_mat_code, ib, ip_plant) values (1, 'GBP','DW', 'C','E', 'GBP');<br />

insert into pod(po_NUM,op_plant, sc, i_mat_code, ib, ip_plant) values (2, 'GBP','DW', 'C','F', 'GBP');<br />

insert into pod(po_NUM,op_plant, sc, i_mat_code, ib, ip_plant) values (2, 'GBP','DW', 'C','G', 'GBP');<br />

insert into pod(po_NUM,op_plant, sc, i_mat_code, ib, ip_plant) values (4, 'GBP','DW', 'C','H', 'GBP');<br />

insert into pod(po_NUM,op_plant, sc, i_mat_code, ib, ip_plant) values (5, 'GBP','DW', 'C','I', 'GBP');<br />

insert into pod(po_NUM,op_plant, sc, i_mat_code, ib, ip_plant) values (5, 'GBP','DW', 'C','J', 'GBP');<br />

insert into pod(po_NUM,op_plant, sc, i_mat_code, ib, ip_plant) values (7, 'GBP','DW', 'C','L', 'GBP');<br />

insert into pod(po_NUM,op_plant, sc, i_mat_code, ib, ip_plant) values (7, 'GBP','DW', 'C','K', 'GBP');<br />

insert into pod(po_NUM,op_plant, sc, i_mat_code, ib, ip_plant) values (7, 'GBP','DW', 'C','M', 'GBP');<br />

insert into pod(po_NUM,op_plant, sc, i_mat_code, ib, ip_plant) values (9, 'GBP','DW', 'C','P', 'GBP');<br />

insert into pod(po_NUM,op_plant, sc, i_mat_code, ib, ip_plant) values (11, 'GBP','DW', 'C','J', 'GBP');<br />

insert into pod(po_NUM,op_plant, sc, i_mat_code, ib, ip_plant) values (13, 'GBP','DW', 'C','M', 'GBP');<br />

insert into pod(po_NUM,op_plant, sc, i_mat_code, ib, ip_plant) values (14, 'GBP','DW', 'C','M', 'GBP');<br />

insert into pod(po_NUM,op_plant, sc, i_mat_code, ib, ip_plant) values (16, 'GBP','DW', 'C','O', 'GBP');<br />

insert into pod(po_NUM,op_plant, sc, i_mat_code, ib, ip_plant) values (16, 'GBP','DW', 'C','N', 'GBP');<br />

insert into pod(po_NUM,op_plant, sc, i_mat_code, ib, ip_plant) values (17, 'GBP','DW', 'C','E', 'GBP');<br />

expected output:<br />

M--C--GBP 1 1 2 16/02/2007 19:25:18 16/02/2007 19:25:18<br />

Q--C--GBP 1 1 1 16/02/2007 19:25:18 16/02/2007 19:25:18<br />

N--C--GBP 2 1 3 16/02/2007 19:25:18 16/02/2007 19:25:18<br />

E--C--GBP 2 1 2 16/02/2007 19:25:18 16/02/2007 19:25:18<br />

B--C--GBP 2 1 1 16/02/2007 19:25:18 16/02/2007 19:25:18<br />

M--C--GBP 2 2 4 16/02/2007 19:25:18 16/02/2007 19:25:18<br />

O--C--GBP 2 2 3 16/02/2007 19:25:18 16/02/2007 19:25:18<br />

E--C--GBP 2 2 2 16/02/2007 19:25:18 16/02/2007 19:25:18<br />

B--C--GBP 2 2 1 16/02/2007 19:25:18 16/02/2007 19:25:18<br />

I--C--GBP 3 1 5 16/02/2007 19:25:19 16/02/2007 19:25:19<br />

H--C--GBP 3 1 4 16/02/2007 19:25:19 16/02/2007 19:25:19<br />

F--C--GBP 3 1 3 16/02/2007 19:25:19 16/02/2007 19:25:19<br />

C--C--GBP 3 1 2 16/02/2007 19:25:19 16/02/2007 19:25:19<br />

A--C--GBP 3 1 1 16/02/2007 19:25:19 16/02/2007 19:25:19<br />

P--C--GBP 3 2 7 16/02/2007 19:25:19 16/02/2007 19:25:19<br />

K--C--GBP 3 2 6 16/02/2007 19:25:19 16/02/2007 19:25:19<br />

J--C--GBP 3 2 5 16/02/2007 19:25:19 16/02/2007 19:25:19<br />

H--C--GBP 3 2 4 16/02/2007 19:25:19 16/02/2007 19:25:19<br />

F--C--GBP 3 2 3 16/02/2007 19:25:19 16/02/2007 19:25:19<br />

C--C--GBP 3 2 2 16/02/2007 19:25:19 16/02/2007 19:25:19<br />

A--C--GBP 3 2 1 16/02/2007 19:25:19 16/02/2007 19:25:19<br />

L--C--GBP 3 3 6 16/02/2007 19:25:19 16/02/2007 19:25:19<br />

J--C--GBP 3 3 5 16/02/2007 19:25:19 16/02/2007 19:25:19<br />

H--C--GBP 3 3 4 16/02/2007 19:25:19 16/02/2007 19:25:19<br />

F--C--GBP 3 3 3 16/02/2007 19:25:19 16/02/2007 19:25:19<br />

C--C--GBP 3 3 2 16/02/2007 19:25:19 16/02/2007 19:25:19<br />

A--C--GBP 3 3 1 16/02/2007 19:25:19 16/02/2007 19:25:19<br />

56 rows.<br />

total 56 rows expected <strong>for</strong> <strong>the</strong> above input.<br />

The procedure is given below. Please help me to minimize <strong>the</strong> below query to execute fast and give <strong>the</strong> result.<br />

CREATE OR REPLACE PROCEDURE hierarchy_Proc IS


CREATE OR REPLACE PROCEDURE hierarchy_Proc IS<br />

tempvar varchar2(255);<br />

batch_cnt number :=1;<br />

sort_cnt number;<br />

cnt number;<br />

old_rec varchar2(255):='TRY';<br />

BranchNoV number :=1;<br />

Key1V number :=1;<br />

Key2V number :=1;<br />

InputV varchar2(255);<br />

ibiv varchar2(255);<br />

obiv varchar2(255);<br />

bgsseq number;<br />

minsort number;<br />

bgs_chk number;<br />

outbatch number;<br />

row_chk number;<br />

cursor otb_id is select obi from oo;<br />

cursor ob_id is select obi from oo where obi in (select obi from oo GROUP BY obi having count(*) > 1);<br />

BEGIN<br />

-- P 1<br />

execute immediate 'truncate table bio';<br />

execute immediate 'truncate table oo';<br />

execute immediate 'truncate table oi';<br />

-- Load <strong>the</strong> bio table with data from <strong>the</strong> po table<br />

insert into bio (select (po.batch_num||'--'||po.mat_code||'--'||po.op_plant) obi, (pod.input_batch||'--'||pod.i_mat_code||'--<br />

'||pod.ip_plant) ibi, po.load_date,po.update_date<br />

from po, pod<br />

where pod.order_num=po.order_num<br />

and pod.op_plant=po.op_plant );<br />

commit;<br />

-- Remove Circular data from bio table<br />

delete from bio where obi in(select a.obi from<br />

bio a inner join bio b<br />

on a.ibi=b.obi and a.obi=b.ibi );<br />

commit;<br />

--Remove duplicate data from bio table<br />

delete from bio where rowid not in<br />

(SELECT MIN(rowid)<br />

FROM bio<br />

GROUP BY obi, ibi);<br />

commit;<br />

-- For all records where <strong>the</strong> ibi does not exist as an obi<br />

For rec in (select distinct(ibi) from (select a.ibi from bio a minus select b.obi from bio b))<br />

loop<br />

insert into bio values(rec.ibi,null,to_date(sysdate,'dd/mm/yyyy'),to_date(sysdate,'dd/mm/yyyy'));<br />

end loop;<br />

commit;<br />

insert into oo(sort_num,obi,ibi,load_date,update_date) (select rownum,obi,ibi,load_date,update_date from bio);<br />

commit;<br />

-- load oi table<br />

insert into oi(sort_num,obi,oi,load_date,update_date)(select sort_num,obi,sort_num,load_date,update_date from oo );<br />

commit;<br />

-- load oi column in <strong>the</strong> oi table <strong>for</strong> obi<br />

For ob_rec in ob_id<br />

loop<br />

select min(sort_num) into minsort from oo where obi =ob_rec.obi;<br />

update oi set oi=minsort where obi=ob_rec.obi;<br />

end loop;<br />

commit;<br />

-- load o_count column in <strong>the</strong> oi table <strong>for</strong> obi<br />

cnt:=1;<br />

sort_cnt:=1;


sort_cnt:=1;<br />

For otb_rec in otb_id loop<br />

if(old_rec = otb_rec.obi) <strong>the</strong>n<br />

cnt:=cnt+1;<br />

update oi set o_count=cnt where obi =otb_rec.obi and sort_num=sort_cnt;<br />

sort_cnt:=sort_cnt+1;<br />

else<br />

cnt:=1;<br />

update oi set o_count=cnt where obi=otb_rec.obi and sort_num=sort_cnt;<br />

sort_cnt:=sort_cnt+1;<br />

end if;<br />

old_rec:=otb_rec.obi;<br />

end loop;<br />

commit;<br />

--Phase 2<br />

execute immediate 'truncate table bgs';<br />

execute immediate 'truncate table bgr';<br />

<br />

For eocbid in ( select distinct(obi) from bio where obi not in (select nvl(ibi,0) from bio)) loop<br />

obiv := eocbid.obi;<br />

<br />

select count(*) into bgs_chk from bgs where obi=obiv;<br />

if (bgs_chk=0) <strong>the</strong>n<br />

goto label3;<br />

else<br />

goto label4;<br />

end if;<br />

<br />

bgsseq :=1;<br />

BranchNoV := bgsseq;<br />

insert into bgs(obi,seq_num,load_date,update_date) values (obiv,bgsseq,sysdate,sysdate);<br />

commit;<br />

goto label5;<br />

<br />

select count(obi) into outbatch from bgs where obi=obiv;<br />

if(outbatch=0) <strong>the</strong>n<br />

bgsseq :=1;<br />

BranchNoV := bgsseq;<br />

insert into bgs(obi,seq_num,load_date,update_date) values (obiv,bgsseq,sysdate,sysdate);<br />

commit;<br />

else<br />

select bgs.seq_num+1 into BranchNoV from bgs bgs where bgs.obi=obiv;<br />

end If;<br />

<br />

select count(*) into row_chk from oi oi where oi.obi=obiv and oi.o_count=BranchNoV;<br />

If(row_chk0) <strong>the</strong>n<br />

select ibi into InputV from oo oo where oo.sort_num=(select distinct(oi.oi + BranchNoV - 1) from oi oi where oi.obi= obiv);<br />

If InputRetV is null <strong>the</strong>n<br />

insert into bgr(batch_num,Key_1,Key_2,gen_sequence,load_date,update_date) (SELECT obi, Key1V,Key2V, (select<br />

count(*) from bgs)+1-LEVEL,sysdate,sysdate FROM bgs start with ibi is null CONNECT BY PRIOR obi= ibi );<br />

<br />

ibiv:=obiv;<br />

commit;<br />

delete from bgs bgs where bgs.obi= obiv;<br />

select obi into obiv from bgs bgs where bgs.ibi =ibiv ;<br />

Key2V := Key2V + 1;<br />

commit;<br />

goto label2;<br />

else<br />

update bgs bgs set bgs.ibi=InputV, bgs.seq_num = BranchNoV,update_date=sysdate where bgs.obi = obiv;


update bgs bgs set bgs.ibi=InputV, bgs.seq_num = BranchNoV,update_date=sysdate where bgs.obi = obiv;<br />

obiv := InputRetV;<br />

commit;<br />

goto label2;<br />

end If;<br />

else<br />

ibiv := obiv;<br />

delete from bgs bgs where bgs.obi=obiv;<br />

commit;<br />

select count(*) into bgs_chk from bgs bgs;<br />

If(bgs_chk = 0) <strong>the</strong>n<br />

Key1V := Key1V + 1;<br />

Key2V :=1;<br />

else<br />

select obi into obiv from bgs bgs where bgs.ibi=ibiv;<br />

goto label2;<br />

end if;<br />

end if;<br />

end loop;<br />

END ;<br />

/<br />

table script<br />

create table bio<br />

(<br />

obi varchar2(255),<br />

ibi varchar2(255),<br />

load_date date,<br />

update_date date)<br />

create table oo(<br />

sort_num number,<br />

obi varchar2(255),<br />

ibi varchar2(255),<br />

load_date date,<br />

update_date date)<br />

create table oi(<br />

obi varchar2(255),<br />

oi number,<br />

output_count number,<br />

load_date date,<br />

update_date date)<br />

create table bgs(<br />

obi varchar2(255),<br />

ibi varchar2(255),<br />

seq_num number,<br />

load_date date,<br />

update_date date)<br />

create table bgr(<br />

batch_num varchar2(255),<br />

key_1 number,<br />

key_2 number,<br />

gen_sequence number,<br />

load_date date,<br />

update_date date)<br />

<strong>Thanks</strong> in advance.<br />

Followup February 18, 2007 - 8am Central time zone:<br />

... sometimes sorting fails. ..<br />

NO, it does not. You might have coded something incorrectly, but "sometimes sorting fails" is not an accurate statement to<br />

make.


make.<br />

but I would like you to read this posting you made, and - pretend you didn't know <strong>the</strong> problem attempting to be solved at all<br />

(sort of like us <strong>for</strong> example) and ask yourself "would anyone really be able to tell what I was trying to do given what I've<br />

posted - did I give <strong>the</strong>m a clear definition of <strong>the</strong> problem in written words"<br />

<strong>the</strong> answer to that is "absolutely not, <strong>the</strong>re is no problem definition, <strong>the</strong>re is however a lot of code that is known to not<br />

function correctly from which one could learn how not to solve <strong>the</strong> as yet to be stated problem"....<br />

Great February 16, 2007 - 8pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Reader from BOSTON<br />

Tom<br />

You should take off from work and provide <strong>the</strong> answer :)<br />

Different Result March 13, 2007 - 4pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Chi from Peekskill, NY<br />

Tom:<br />

I ran <strong>the</strong> following SQL on my development & production servers and got different results:<br />

select 1 from plateau.pa_student a<br />

where a.stud_id = 'adhh01'<br />

and a.stud_id in<br />

(select b.stud_id<br />

from plateau.pa_student b start with b.stud_id = 'deaalder' <strong>connect</strong> <strong>by</strong> prior b.super =<br />

b.stud_id<br />

union select c.stud_id<br />

from plateau.pa_student c start with c.stud_id = 'deaalder' <strong>connect</strong> <strong>by</strong> c.super = prior<br />

c.stud_id )<br />

Both servers are running 10.2.0.3 on Windows 2003 and all instances are created using DBCA. On <strong>the</strong> development server<br />

<strong>the</strong> result was as expected: "no rows selected". However on <strong>the</strong> production server it returned "1". I have compared all<br />

parameters on <strong>the</strong>se 2 instances and found no mismatch (except those include <strong>the</strong> path). The problem is with <strong>the</strong> server<br />

as I tested on ano<strong>the</strong>r instance on <strong>the</strong> production server and got same incorrest result. What would cause this kind of<br />

discrepancy? <strong>Thanks</strong> <strong>for</strong> <strong>the</strong> help.<br />

Followup March 13, 2007 - 8pm Central time zone:<br />

perhaps different data...<br />

but, you give us nothing to work with.<br />

are <strong>the</strong> plans <strong>the</strong> same?<br />

Display all dates in a year March 19, 2007 - 3pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Jay from Herndon, VA<br />

Good day Tom!<br />

I honestly am hoping that I am not posting this <strong>question</strong> in a wrong thread.<br />

Tom, I am trying to just write a query to give me all <strong>the</strong> dates from a particular date to <strong>the</strong> system date.<br />

For instance, if I enter 01/01/2007, I need a query to fetch me records like this...<br />

01/01/2007<br />

01/02/2007<br />

01/03/2007<br />

01/04/2007<br />

...<br />

..<br />

03/19/2007<br />

Is this something simple to do? Do we need to take into consideration <strong>the</strong> leap year issue as well or will oracle know <strong>the</strong><br />

dates?<br />

<strong>Thanks</strong> Tom. Have a good one!


Followup March 19, 2007 - 7pm Central time zone:<br />

ops$tkyte%ORA10GR2> with data<br />

2 as<br />

3 (select level-1 l<br />

4 from dual<br />

5 <strong>connect</strong> <strong>by</strong> level <br />

March 20, 2007 - 5am Central time zone Bookmark | Bottom | Top<br />

Reviewer: A reader<br />

Tom,<br />

Can you explain <strong>the</strong> logic behind<br />

'<strong>connect</strong> <strong>by</strong> level < n' when <strong>the</strong> from clause has Dual or a Single Row rowsource.<br />

Looks like <strong>the</strong> <strong>connect</strong> <strong>by</strong> keeps pumping rows because it cannot decide a relationship between parent and child rows.<br />

Is it like if it can't find a relationship, it will assume each row to be a parent as well as a child <strong>for</strong> every level?<br />

2) Why did you not choose to do it to a multi row table?<br />

<strong>Thanks</strong><br />

Ravi<br />

Followup March 20, 2007 - 7am Central time zone:<br />

with data<br />

as<br />

(select level from dual <strong>connect</strong> <strong>by</strong> level < :n )<br />

is just a way to create :N rows - <strong>the</strong> <strong>connect</strong> <strong>by</strong> statement is satisfied as long as level is less than N - so it gets <strong>the</strong> first row<br />

from dual, sets level to 1 and says "1 is less than :n, great, we <strong>the</strong>re<strong>for</strong>e create a new level and try again, now level is 2....."<br />

2) because I needed N rows and dual is perfect <strong>for</strong> that? If I picked a 'real table', I'd have to pick on with at least :n rows - but<br />

we don't know what :n is<br />

wow! March 20, 2007 - 8am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Jay from Herndon, VA<br />

<strong>Thanks</strong> Tom!<br />

Jay<br />

Kindly help SQL <strong>for</strong> Chain Marketing system - March 31, 2007 - 10am Central time zone<br />

Bookmark | Bottom | Top<br />

Reviewer: Arindam Mukherjee from Kolkata, India<br />

Chain Marketing system, one has so many workers. That worker also has so many workers. In this way, we get so many<br />

workers down <strong>the</strong> line <strong>for</strong>ming a chain. Our table structure looks like below. The total no. of records in this table is equal to<br />

<strong>the</strong> no. of agents. So Agent_id and Introducer_id are self_referential.<br />

Agent_id | Commission | GAP-Commi| Introducer_id | Introducer_rank | team_strength<br />

Data looks like


1st row >> 100 | 15% | 0 | 91 | 2 | 0<br />

2nd row >> 91 | 5% | 0 | 81 | 3 | 7<br />

3rd row >> 81 | 2% | 3% |61 | 5 | 9 --- here 4th rank is missing because of termination.<br />

31st row >> 300 | 15% | 0 | 51 | 2 | 0<br />

32nd row >> 51 | 5% | 0 | 47 | 3 | 18<br />

33rd row >> 47 | 1% | 3% + 2% |33 | 6 | 56 --- here 4th and 5th rank is missing because of termination.<br />

We need <strong>the</strong> following results.<br />

1 > Each rank and its strength down <strong>the</strong> line, suppose <strong>for</strong> agent ID = 81, we need<br />

For 81, 9 and <strong>for</strong> 91, 7 as 91 is related to 81.<br />

2> When one agent brings one business say $100, commission column will be updated as follows.<br />

1st row >> 15% of 100 = 15 (Ei<strong>the</strong>r insert or update with existing one)<br />

2nd row >> 5% of 100 = 5 (Ei<strong>the</strong>r insert or update with existing one)<br />

3rd row >> 2% of 100 = 2 (Ei<strong>the</strong>r insert or update with existing one)<br />

3rd row >> Gap Commission 3% of 100 = 3 (Ei<strong>the</strong>r insert or update with existing one)<br />

Since 4th Rank does not exist, 5th rank will get Gap commission.<br />

You have every right to change <strong>the</strong> table structure but please help us write <strong>the</strong>se complicated SQL as <strong>the</strong> table will have<br />

more than 40 thousands record. We are trying " <strong>connect</strong> <strong>by</strong> " clause but could not get success. Our database is Oracle 9i.<br />

Kindly help us.<br />

April 1, 2007 - 11pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Arindam Mukherjee from Kolkata, India<br />

Sir,<br />

You please read <strong>the</strong> above facts and kindly guide me how to calculate commission when one business of $100 would be<br />

in place. So I need Insert / Update and Query SQL, Please help me.<br />

Per<strong>for</strong>mance of <strong>connect</strong> <strong>by</strong> April 13, 2007 - 8am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Artur Popov from Ukhta, Komi, Russian Federation<br />

Hi, Tom.<br />

I have a big problem with two queries and I want to understand why do <strong>the</strong>ir execution time differs so much. Here <strong>the</strong>y are:<br />

SELECT ap.app_id, ap.created, u.fio_short from_user, ut.fio_short to_user, ty.TYPE_NAME, ap.app_num<br />

FROM applications_events ae, apps ap, application_types_ref ty, isa_own.users u, isa_own.users ut<br />

WHERE ae.application_id = ap.app_id<br />

AND ae.event_time = ap.recent_event_time<br />

AND ae.event_id = 3<br />

AND ae.user_id IN<br />

(SELECT id_user<br />

FROM isa_own.users<br />

WHERE pr_rabot = 1 START WITH id_user = 363 CONNECT BY PRIOR id_user = id_nach)<br />

AND u.id_user = ap.creator_id<br />

AND ut.id_user = ae.user_id<br />

AND ap.TYPE_ID = ty.TYPE_ID;<br />

It takes over 5 seconds to execute. But when I replaced <strong>the</strong> subquery with <strong>connect</strong> <strong>by</strong> with it's result:<br />

SELECT ap.app_id, ap.created, u.fio_short from_user, ut.fio_short to_user, ty.TYPE_NAME, ap.app_num<br />

FROM applications_events ae, apps ap, application_types_ref ty, isa_own.users u, isa_own.users ut<br />

WHERE ae.application_id = ap.app_id<br />

AND ae.event_time = ap.recent_event_time<br />

AND ae.event_id = 3<br />

AND ae.user_id IN<br />

(363,359,361,364,341,354,590,591,944,840)<br />

AND u.id_user = ap.creator_id<br />

AND ut.id_user = ae.user_id<br />

AND ap.TYPE_ID = ty.TYPE_ID


<strong>the</strong> execution time became just 0.9s.<br />

Why does it happens and how can I speed up my first query?<br />

Followup April 13, 2007 - 2pm Central time zone:<br />

did you look at <strong>the</strong> plans to see what is fundamentally different<br />

and how long does <strong>the</strong> <strong>connect</strong> <strong>by</strong> take itself.<br />

Per<strong>for</strong>mance of <strong>connect</strong> <strong>by</strong> April 16, 2007 - 12am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Popov Artur from Ukhta, Komi, Russian Federation<br />

Hi Tom.<br />

Here are <strong>the</strong> plans <strong>for</strong> my queries from <strong>the</strong> previous <strong>question</strong>.<br />

First query:<br />

Rows Row Source Operation<br />

------- ---------------------------------------------------<br />

128 HASH JOIN (cr=6551 r=5769 w=1529 time=5676858 us)<br />

128 HASH JOIN (cr=6518 r=5769 w=1529 time=5670442 us)<br />

128 HASH JOIN (cr=6487 r=5769 w=1529 time=5664359 us)<br />

128 MERGE JOIN (cr=6480 r=5769 w=1529 time=5662939 us)<br />

4032 SORT JOIN (cr=6428 r=5769 w=1529 time=5615042 us)<br />

4032 MERGE JOIN SEMI (cr=6428 r=5769 w=1529 time=5586127 us)<br />

384128 SORT JOIN (cr=6363 r=5769 w=1529 time=4003999 us)<br />

384128 TABLE ACCESS FULL APPLICATIONS_EVENTS (cr=6363 r=4240 w=0 time=907248 us)<br />

4032 SORT UNIQUE (cr=65 r=0 w=0 time=609225 us)<br />

10 VIEW (cr=65 r=0 w=0 time=13402 us)<br />

10 FILTER (cr=65 r=0 w=0 time=13379 us)<br />

11 CONNECT BY WITH FILTERING (cr=65 r=0 w=0 time=13344 us)<br />

1 NESTED LOOPS (cr=3 r=0 w=0 time=133 us)<br />

1 INDEX UNIQUE SCAN PK_ID_USER (cr=2 r=0 w=0 time=72 us)(object id 10236)<br />

1 TABLE ACCESS BY USER ROWID USERS (cr=1 r=0 w=0 time=49 us)<br />

10 HASH JOIN (cr=62 r=0 w=0 time=12927 us)<br />

11 CONNECT BY PUMP (cr=0 r=0 w=0 time=92 us)<br />

1920 TABLE ACCESS FULL USERS (cr=62 r=0 w=0 time=4132 us)<br />

128 SORT JOIN (cr=52 r=0 w=0 time=37592 us)<br />

6000 TABLE ACCESS FULL APPS (cr=52 r=0 w=0 time=9611 us)<br />

2 TABLE ACCESS FULL APPLICATION_TYPES_REF (cr=7 r=0 w=0 time=190 us)<br />

960 TABLE ACCESS FULL USERS (cr=31 r=0 w=0 time=1601 us)<br />

960 TABLE ACCESS FULL USERS (cr=33 r=0 w=0 time=1665 us)<br />

Second query:<br />

Rows Row Source Operation<br />

------- ---------------------------------------------------<br />

128 NESTED LOOPS (cr=2583 r=28 w=0 time=144063 us)<br />

128 NESTED LOOPS (cr=2317 r=28 w=0 time=141186 us)<br />

128 HASH JOIN (cr=2179 r=28 w=0 time=138167 us)<br />

4032 TABLE ACCESS BY INDEX ROWID APPLICATIONS_EVENTS (cr=2125 r=28 w=0 time=78225 us)<br />

4363 NESTED LOOPS (cr=82 r=0 w=0 time=36619 us)<br />

10 INLIST ITERATOR (cr=31 r=0 w=0 time=546 us)<br />

10 TABLE ACCESS BY INDEX ROWID USERS (cr=31 r=0 w=0 time=416 us)<br />

10 INDEX RANGE SCAN PK_ID_USER (cr=21 r=0 w=0 time=265 us)(object id 10236)<br />

4352 INDEX RANGE SCAN T_INDEX3 (cr=51 r=0 w=0 time=16801 us)(object id 29522)<br />

6000 TABLE ACCESS FULL APPS (cr=54 r=0 w=0 time=8504 us)<br />

128 TABLE ACCESS BY INDEX ROWID APPLICATION_TYPES_REF (cr=138 r=0 w=0 time=2066 us)<br />

128 INDEX UNIQUE SCAN PK_APPLICATIONS_TYPES_REF (cr=10 r=0 w=0 time=751 us)(object id 9935)<br />

128 TABLE ACCESS BY INDEX ROWID USERS (cr=266 r=0 w=0 time=1995 us)<br />

128 INDEX UNIQUE SCAN PK_ID_USER (cr=138 r=0 w=0 time=942 us)(object id 10236)<br />

We can see, that <strong>the</strong>y are completely different, but I didn't change <strong>the</strong> query, just replaced <strong>the</strong> subquery with it's result. Why<br />

did it cause such consequences?<br />

Followup April 16, 2007 - 1pm Central time zone:<br />

so, in <strong>the</strong> bad plan - are <strong>the</strong> estimated card= values near to <strong>the</strong> actuals you posted here.<br />

NO_FILTER Hint - What is it ? April 16, 2007 - 12am Central time zone Bookmark | Bottom | Top


NO_FILTER Hint - What is it ? April 16, 2007 - 12am Central time zone Bookmark | Bottom | Top<br />

Reviewer: BC from Macomb Twp<br />

Tom,<br />

What is <strong>the</strong> NO_FILTER hint ? I have seen it used in several queries using CONNECT BY.<br />

<strong>Thanks</strong><br />

BC<br />

Per<strong>for</strong>mance of <strong>connect</strong> <strong>by</strong> April 17, 2007 - 12am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Popov Artur from Ukhta, Komi, Russian Federation<br />

Statistics <strong>for</strong> all tables is ga<strong>the</strong>red and actual.<br />

For <strong>the</strong> first query I got this plan (using autotrace):<br />

0 SELECT STATEMENT Optimizer=CHOOSE (Cost=514 Card=16 Bytes=2656)<br />

1 0 HASH JOIN (Cost=514 Card=16 Bytes=2656)<br />

2 1 HASH JOIN (Cost=509 Card=16 Bytes=2368)<br />

3 2 HASH JOIN (Cost=504 Card=16 Bytes=2080)<br />

4 3 MERGE JOIN (Cost=501 Card=16 Bytes=1328)<br />

5 4 SORT (JOIN) (Cost=435 Card=192224 Bytes=6727840)<br />

6 5 MERGE JOIN (SEMI) (Cost=435 Card=192224 Bytes=6727840)<br />

7 6 SORT (JOIN) (Cost=427 Card=192224 Bytes=4228928)<br />

8 7 TABLE ACCESS (FULL) OF 'T' (Cost=427 Card=192224 Bytes=4228928)<br />

9 6 SORT (UNIQUE) (Cost=8 Card=960 Bytes=12480)<br />

10 9 VIEW OF 'VW_NSO_1' (Cost=4 Card=960 Bytes=12480)<br />

11 10 FILTER<br />

12 11 CONNECT BY (WITH FILTERING)<br />

13 12 NESTED LOOPS<br />

14 13 INDEX (UNIQUE SCAN) OF 'PK_ID_USER' (UNIQUE) (Cost=1 Card=1<br />

Bytes=4)<br />

15 13 TABLE ACCESS (BY USER ROWID) OF 'USERS'<br />

16 12 HASH JOIN<br />

17 16 CONNECT BY PUMP<br />

18 16 TABLE ACCESS (FULL) OF 'USERS' (Cost=4 Card=960 Bytes=10560)<br />

19 4 SORT (JOIN) (Cost=66 Card=6000 Bytes=288000)<br />

20 19 TABLE ACCESS (FULL) OF 'APPS' (Cost=7 Card=6000 Bytes=288000)<br />

21 3 TABLE ACCESS (FULL) OF 'APPLICATION_TYPES_REF' (Cost=2 Card=2 Bytes=94)<br />

22 2 TABLE ACCESS (FULL) OF 'USERS' (Cost=4 Card=960 Bytes=17280)<br />

23 1 TABLE ACCESS (FULL) OF 'USERS' (Cost=4 Card=960 Bytes=17280)<br />

Ok, to make everything clear here is my tables:<br />

-- 6000 rows.<br />

create table APPS<br />

(<br />

APP_ID NUMBER,<br />

TYPE_ID NUMBER not null,<br />

CREATOR_ID NUMBER(38) not null,<br />

CREATED TIMESTAMP(6) not null,<br />

POSSIBLE_NAPR_ID NUMBER not null,<br />

RECENT_EVENT_TIME TIMESTAMP(4),<br />

APP_NUM VARCHAR2(14)<br />

);<br />

alter table APPS add constraint PK_APPS primary key (APP_ID);<br />

alter table APPS add constraint FK_APPS_NAPR <strong>for</strong>eign key (POSSIBLE_NAPR_ID)<br />

references ISA_OWN.NAPRAVL (ID_NAPR);<br />

alter table APPS add constraint FK_APPS_TYPES <strong>for</strong>eign key (TYPE_ID)<br />

references APPLICATION_TYPES_REF (TYPE_ID);<br />

alter table APPS add constraint FK_APPS_USERS <strong>for</strong>eign key (CREATOR_ID)<br />

references ISA_OWN.USERS (ID_USER);<br />

-- The following table is made just <strong>for</strong> testing.<br />

-- It have 768383 rows.<br />

create table APPLICATIONS_EVENTS<br />

(<br />

EVENT_TIME TIMESTAMP(4) not null,<br />

EVENT_ID NUMBER not null,<br />

APPLICATION_ID NUMBER not null,<br />

USER_ID NUMBER,<br />

REASON VARCHAR2(50),<br />

NAPR_ID NUMBER,


NAPR_ID NUMBER,<br />

ITEM_ID NUMBER<br />

)<br />

create index T_INDEX1 on APPLICATIONS_EVENTS (EVENT_TIME);<br />

create index T_INDEX2 on APPLICATIONS_EVENTS (APPLICATION_ID);<br />

create index T_INDEX3 on APPLICATIONS_EVENTS (USER_ID);<br />

-- 2 rows at this moment.<br />

create table APPLICATION_TYPES_REF<br />

(<br />

TYPE_ID NUMBER not null,<br />

TYPE_NAME VARCHAR2(60) not null<br />

);<br />

alter table APPLICATION_TYPES_REF add constraint PK_APPLICATIONS_TYPES_REF primary key (TYPE_ID);<br />

-- 920 rows.<br />

create table ISA_OWN.USERS<br />

(<br />

FNAME VARCHAR2(50) not null,<br />

MNAME VARCHAR2(50) not null,<br />

LNAME VARCHAR2(50) not null,<br />

FIO_SHORT VARCHAR2(60) not null,<br />

ID_USER NUMBER(10) not null,<br />

FOTO BLOB,<br />

KAB VARCHAR2(50),<br />

TEL VARCHAR2(50),<br />

EMAIL VARCHAR2(50),<br />

ID_DOLJNOST NUMBER(10) not null,<br />

ID_NAPR NUMBER(10) not null,<br />

TAB VARCHAR2(5) not null,<br />

FOTO_NAME VARCHAR2(100),<br />

CON_NAME VARCHAR2(50) not null,<br />

PAS VARCHAR2(50) not null,<br />

VID_DOG VARCHAR2(20),<br />

DATE_NAIM DATE,<br />

ID_NACH NUMBER(10),<br />

PR_RABOT NUMBER(1) default 1 not null,<br />

D_R DATE,<br />

TEL_DOM VARCHAR2(50),<br />

PROFSOUZ NUMBER(1) default 1 not null,<br />

POL VARCHAR2(1) not null,<br />

EMAIL_LOC VARCHAR2(50),<br />

NOVELL_NAME VARCHAR2(20)<br />

);<br />

alter table ISA_OWN.USERS add constraint PK_ID_USER primary key (ID_USER);<br />

alter table ISA_OWN.USERS add constraint CON_PAS_UNIQ unique (CON_NAME,PAS);<br />

alter table ISA_OWN.USERS add constraint TAB_UNIQ unique (TAB);<br />

create index ISA_OWN.IND_ID_NACH on ISA_OWN.USERS (ID_NACH);<br />

My colleagues adviced me to store <strong>the</strong> results of <strong>the</strong> '<strong>connect</strong> <strong>by</strong>' subquery in a temporary table and replace <strong>the</strong> '<strong>connect</strong> <strong>by</strong>'<br />

subquery with simple select from this table, but I think this is not <strong>the</strong> best idea. What can you say?<br />

Followup April 17, 2007 - 9am Central time zone:<br />

I simply asked:<br />

so, in <strong>the</strong> bad plan - are <strong>the</strong> estimated card= values near to <strong>the</strong> actuals you posted here.<br />

Per<strong>for</strong>mance of <strong>connect</strong> <strong>by</strong> April 17, 2007 - 11am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Popov Artur from Ukhta, Komi, Russian Federation<br />

As you can see <strong>the</strong>y are close, excluding APPLICATIONS_EVENTS. I just wanted to give you better understanding of <strong>the</strong><br />

problem.<br />

So, can you help?<br />

Connect <strong>by</strong> prior July 18, 2007 - 11am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Bob from London, UK<br />

Hi Tom,<br />

Is <strong>the</strong>re a way of listing parent and child relations in a query. For example, if Site 1: has child sites 2,3,4,5,6.<br />

I want to able to select:<br />

1,2


1,2<br />

1,3<br />

1,4<br />

1,5<br />

1,6<br />

and so on..<br />

<strong>Thanks</strong><br />

Followup July 18, 2007 - 12pm Central time zone:<br />

from what do you want to select this<br />

<strong>connect</strong> <strong>by</strong> prior July 18, 2007 - 11am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Bob from London<br />

Good explanation at <strong>the</strong> top, answers my <strong>question</strong>!!!<br />

Connect <strong>by</strong> mecanism June 23, 2008 - 1am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Car Elcaro<br />

I see <strong>the</strong> mecanism of <strong>connect</strong> <strong>by</strong> and join above in this url :<br />

<br />

http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:489772591421#69739221126046<br />

join is done<br />

start with applied<br />

<strong>connect</strong> <strong>by</strong> done<br />

where clause applied to results<br />

I have confusion here :<br />

create table t1<br />

(<br />

key number,<br />

parent varchar2(2)<br />

)<br />

create table t2<br />

(<br />

parent varchar2(2),<br />

child varchar2(2)<br />

)<br />

insert into t1 values (1,'A1');<br />

insert into t1 values (2,'B1');<br />

select * from t1;<br />

KEY PA<br />

---------- --<br />

1 A1<br />

2 B1<br />

insert into t2 values ('A1','A2');<br />

insert into t2 values ('A2','A3');<br />

insert into t2 values ('A3','A4');<br />

select * from t2;<br />

PA CH<br />

-- --<br />

A1 A2<br />

A2 A3<br />

A3 A4<br />

Join between t1 and t2 without any condition<br />

select t1.key, t1.parent, t2.child from t1, t2;


KEY PA CH<br />

---------- -- --<br />

1 A1 A2<br />

2 B1 A2<br />

1 A1 A3<br />

2 B1 A3<br />

1 A1 A4<br />

2 B1 A4<br />

6 rows selected.<br />

Use <strong>connect</strong> <strong>by</strong> with 'start with' clause<br />

column scbp <strong>for</strong>mat a20<br />

select level, key,<br />

sys_<strong>connect</strong>_<strong>by</strong>_path(key || ',' || child ,'|') scbp<br />

from t1, t2<br />

start with t2.parent = t1.parent<br />

<strong>connect</strong> <strong>by</strong> prior t2.child = t2.parent<br />

order <strong>by</strong> level<br />

LEVEL KEY SCBP<br />

---------- ---------- --------------------<br />

1 1 |1,A2<br />

2 2 |1,A2|2,A3<br />

2 1 |1,A2|1,A3<br />

3 1 |1,A2|2,A3|1,A4<br />

3 2 |1,A2|2,A3|2,A4<br />

3 1 |1,A2|1,A3|1,A4<br />

3 2 |1,A2|1,A3|2,A4<br />

7 rows selected.<br />

Here I only see one level 1, why ? My reason asking here is that you said join first and <strong>the</strong>n 'start with' clause executed and<br />

from intermediate join result between t1 and t2 with parent = 'A1' is 3 record.<br />

select * from<br />

(<br />

select t1.key, t1.parent, t2.child from t1, t2<br />

)<br />

where parent = 'A1'<br />

KEY PA CH<br />

---------- -- --<br />

1 A1 A2<br />

1 A1 A3<br />

1 A1 A4<br />

Questions :<br />

1. Why Oracle not choose A3 as a level one (see SCBP result above) ? Does it just lucky ? Is Oracle implicitly add predicate<br />

key = 1 as parent = 'A1' is originated from key = 1 from table t1 ?<br />

2. Why only 1 level one not three ?<br />

3. Query to show output like below - of course without pl/sql :<br />

--insert first at table t2<br />

insert into t2 values ('B1','B2');<br />

insert into t2 values ('B2','B3');<br />

insert into t2 values ('B3','B4');<br />

KEY ID<br />

---------- ----------<br />

1 A2<br />

1 A3<br />

1 A4<br />

2 B2<br />

2 B3<br />

2 B4


<strong>Thanks</strong>. <br />

Followup June 23, 2008 - 7am Central time zone:<br />

You queried:<br />

select t1.key, t1.parent, t2.child from t1, t2;<br />

and <strong>the</strong>n you<br />

select level, key,<br />

sys_<strong>connect</strong>_<strong>by</strong>_path(key || ',' || child ,'|') scbp<br />

from t1, t2<br />

start with t2.parent = t1.parent<br />

<strong>connect</strong> <strong>by</strong> prior t2.child = t2.parent<br />

order <strong>by</strong> level<br />

used parent from both (but only queried parent from one) and used t2.parent again, but didn't query it. So, I fail to see why<br />

<strong>the</strong> cartesian join you presented is relevant - it isn't showing most of <strong>the</strong> data you use.<br />

ops$tkyte%ORA10GR2> create table t3<br />

2 as<br />

3 select t1.key t1_key, t1.parent t1_parent, t2.child t2_child, t2.parent t2_parent from t1, t2;<br />

Table created.<br />

ops$tkyte%ORA10GR2><br />

ops$tkyte%ORA10GR2> select * from t3;<br />

T1_KEY T1_PARENT T2_CHILD T2_PARENT<br />

---------- --------- -------- ---------<br />

1 A1 A2 A1<br />

1 A1 A3 A2<br />

1 A1 A4 A3<br />

2 B1 A2 A1<br />

2 B1 A3 A2<br />

2 B1 A4 A3<br />

6 rows selected.<br />

ops$tkyte%ORA10GR2><br />

ops$tkyte%ORA10GR2> column scbp <strong>for</strong>mat a20<br />

ops$tkyte%ORA10GR2><br />

ops$tkyte%ORA10GR2> select level, key,<br />

2 sys_<strong>connect</strong>_<strong>by</strong>_path(key || ',' || child ,'|') scbp<br />

3 from t1, t2<br />

4 start with t2.parent = t1.parent<br />

5 <strong>connect</strong> <strong>by</strong> prior t2.child = t2.parent<br />

6 order <strong>by</strong> level;<br />

LEVEL KEY SCBP<br />

---------- ---------- --------------------<br />

1 1 |1,A2<br />

2 2 |1,A2|2,A3<br />

2 1 |1,A2|1,A3<br />

3 1 |1,A2|2,A3|1,A4<br />

3 2 |1,A2|2,A3|2,A4<br />

3 1 |1,A2|1,A3|1,A4<br />

3 2 |1,A2|1,A3|2,A4<br />

7 rows selected.<br />

so, start with set is:<br />

ops$tkyte%ORA10GR2> select * from t3 where t2_parent = t1_parent;<br />

T1_KEY T1_PARENT T2_CHILD T2_PARENT


---------- --------- -------- ---------<br />

1 A1 A2 A1<br />

a single row to start with... and <strong>the</strong>n <strong>the</strong> first <strong>connect</strong> <strong>by</strong> level (you said <strong>the</strong>re is only one level, but your example shows<br />

THREE!!!!) would be:<br />

ops$tkyte%ORA10GR2> select * from t3 where t2_parent = 'A2';<br />

T1_KEY T1_PARENT T2_CHILD T2_PARENT<br />

---------- --------- -------- ---------<br />

1 A1 A3 A2<br />

2 B1 A3 A2<br />

and <strong>the</strong>n level three would be:<br />

ops$tkyte%ORA10GR2> select * from t3 where t2_parent = 'A3';<br />

T1_KEY T1_PARENT T2_CHILD T2_PARENT<br />

---------- --------- -------- ---------<br />

1 A1 A4 A3<br />

2 B1 A4 A3<br />

and level four:<br />

ops$tkyte%ORA10GR2> select * from t3 where t2_parent = 'A4';<br />

no rows selected<br />

level 1 <strong>connect</strong>ed to 2 level 2's. Each of <strong>the</strong> level 2's <strong>connect</strong>ed to 2 level 3's. Level 3 was <strong>the</strong> end.<br />

ops$tkyte%ORA10GR2> select rpad('*', 2*level, '*') indent, level, key,<br />

2 sys_<strong>connect</strong>_<strong>by</strong>_path(key || ',' || child ,'|') scbp<br />

3 from t1, t2<br />

4 start with t2.parent = t1.parent<br />

5 <strong>connect</strong> <strong>by</strong> prior t2.child = t2.parent<br />

6 /<br />

INDENT LEVEL KEY SCBP<br />

---------- ---------- ---------- --------------------<br />

** 1 1 |1,A2<br />

**** 2 1 |1,A2|1,A3<br />

****** 3 1 |1,A2|1,A3|1,A4<br />

****** 3 2 |1,A2|1,A3|2,A4<br />

**** 2 2 |1,A2|2,A3<br />

****** 3 1 |1,A2|2,A3|1,A4<br />

****** 3 2 |1,A2|2,A3|2,A4<br />

7 rows selected.<br />

Now, your <strong>question</strong>s don't make sense because <strong>the</strong>y were based on a flawed example (it is easy to see why <strong>the</strong>re is only<br />

one level one once you look at all of <strong>the</strong> columns, <strong>the</strong>re isn't one level, <strong>the</strong>re are THREE and your example shows that)<br />

and <strong>the</strong> kicker <strong>question</strong>, #3, well, please. Tell us <strong>the</strong> logic <strong>the</strong>re. I have no idea what your output represents and given that<br />

<strong>the</strong> example was not very useful - we are really lost in <strong>the</strong> weeds.


e: Connect <strong>by</strong> mecanism June 25, 2008 - 1am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Car Elcaro<br />

assume that t2 is a process table (t2.child) and and t1.key is a row material. both table are<br />

<strong>connect</strong>ed with t1.parent and t2.parent as a starting point and <strong>the</strong>n we just want to see all process<br />

in t2.child that belongs to t1.id.<br />

Here is <strong>the</strong> example<br />

t1.key = 1 t1.parent = 'A1' we get from t2 as starting point is (A1,A2) and <strong>the</strong>n <strong>connect</strong>ed to it<br />

self so resulting A2, A3, A4 as processes that key = 1 is pass.<br />

Followup June 25, 2008 - 8am Central time zone:<br />

this is no example<br />

assume we don't know what t1 looks like (<strong>the</strong>re<strong>for</strong>e, need a create)<br />

assume same <strong>for</strong> t2<br />

assume we haven't any data in ei<strong>the</strong>r of <strong>the</strong>m and need some.<br />

assume "row material" is a meaningless term - I've never heard it.<br />

assume that t1.id and t2.child have some relationship we don't understand "<strong>the</strong>n we just want to see all process in t2.child<br />

that belongs to t1.id." is not meaningful to us.<br />

Build better example please - this one was not intuitive. Don't say "i already gave you tables", you did, but not <strong>for</strong> this<br />

example. t1.id is a new thing here.<br />

ignore <strong>the</strong> existence of <strong>the</strong> prior example, make everything self contained and clear with true examples. Perhaps build it up<br />

<strong>the</strong> way I did to demonstrate how <strong>connect</strong> <strong>by</strong> works above - a bit a time, building layer upon layer showing you what I meant.<br />

completing <strong>question</strong> on <strong>connect</strong> <strong>by</strong> June 28, 2008 - 1am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Car Elcaro<br />

<br />

Sorry <strong>for</strong> incomplete and inconsitent <strong>question</strong> above. Let me reintroduce <strong>the</strong> problem.<br />

I have table t2 defined as follow :<br />

create table t2<br />

( c_inp varchar2(2),<br />

c_out varchar2(2),<br />

c_prc varchar2(2)<br />

);<br />

insert into t2 values ('R1','S1','P1');<br />

insert into t2 values ('S1','T1','P2');<br />

insert into t2 values ('T1','U1','P3');<br />

insert into t2 values ('U1','V1','P4');<br />

insert into t2 values ('R2','S2','P1');<br />

insert into t2 values ('S2','T2','P2');<br />

insert into t2 values ('T2','U2','P3');<br />

insert into t2 values ('U2','V2','P4');<br />

insert into t2 values ('V2','W2','P5');<br />

commit;<br />

column c_inp <strong>for</strong>mat a5;<br />

column c_out <strong>for</strong>mat a5;<br />

column c_prc <strong>for</strong>mat a5;<br />

select * from t2;<br />

C_INP C_OUT C_PRC<br />

----- ----- -----<br />

R1 S1 P1<br />

S1 T1 P2<br />

T1 U1 P3<br />

U1 V1 P4<br />

R2 S2 P1<br />

S2 T2 P2


S2 T2 P2<br />

T2 U2 P3<br />

U2 V2 P4<br />

V2 W2 P5<br />

9 rows selected.<br />

c_inp is RAW MATERIAL or INTERMEDIATE MATERIAL to process specified in c_prc into c_out so each row of this table<br />

mean a process. For example R1 is a RAW MATERIAL processed <strong>by</strong> P1 into S1. Next S1 is an INTERMEDIATE MATERIAL<br />

to process into T1. Similar to say that S1,T1,U1,V1 of c_inp is INTERMEDIATE OUTPUT <strong>for</strong> R1,S1,T1,U1 of c_out<br />

respectively.<br />

I have ano<strong>the</strong>r table that contain who is <strong>the</strong> supplier of raw material like R1 and R2 on <strong>the</strong> table above. Here is <strong>the</strong> structure<br />

:<br />

create table t1<br />

(<br />

c_sup varchar2(4),<br />

c_raw varchar2(2)<br />

);<br />

insert into t1 values ('SUP1','R1');<br />

insert into t1 values ('SUP2','R2');<br />

commit;<br />

column c_sup <strong>for</strong>mat a5;<br />

column c_raw <strong>for</strong>mat a5;<br />

select * from t1;<br />

C_SUP C_RAW<br />

----- -----<br />

SUP1 R1<br />

SUP2 R2<br />

Actually <strong>question</strong> #3 : I want to list supplier with <strong>the</strong>ir INTERMEDIATE OUTPUT so <strong>for</strong> supplier SUP1 I have four intermediate<br />

output (S1,T1,U1,V1) and <strong>for</strong> SUP2 I have five (S2,T2,U2,V2,W2). Expected result :<br />

C_SUP C_OUT<br />

----- -----<br />

SUP1 S1<br />

SUP1 T1<br />

SUP1 U1<br />

SUP1 V1<br />

SUP2 S2<br />

SUP2 T2<br />

SUP2 U2<br />

SUP2 V2<br />

SUP2 W2<br />

9 rows selected.<br />

Currently, my solution use a PL/SQL to solve it. And I search a better solution if it's exist e.g. without using PL/SQL. Here is<br />

my current solution :<br />

create or replace function find_output(p_raw varchar2)<br />

return varchar2<br />

is<br />

l_output varchar2(20);<br />

begin<br />

select max(sys_<strong>connect</strong>_<strong>by</strong>_path(c_out,',') || ',')<br />

into l_output<br />

from t2<br />

start with c_inp = p_raw<br />

<strong>connect</strong> <strong>by</strong> c_inp = prior c_out;<br />

return l_output;


eturn l_output;<br />

end;<br />

/<br />

select c_sup, column_value c_out<br />

from (select c_sup, find_output(c_raw) c_list from t1) a,<br />

table(<br />

cast(<br />

multiset(<br />

(select substr(c_list,instr(c_list,',',1,level)+1,<br />

instr(c_list,',',1,level+1)instr(c_list,',',1,level)-1)<br />

from dual<br />

<strong>connect</strong> <strong>by</strong> level


more efficient.<br />

To : Car Elcaro July 2, 2008 - 11am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Raj from United Kingdom<br />

Tom,<br />

First and <strong>for</strong>emost I thank you <strong>for</strong> your wonderful support to <strong>the</strong> oracle world. Correct if I am wrong.<br />

Why can't we do it this way. I assumed <strong>the</strong> OP is using oracle 10g or above.<br />

SQL> select * from v$<strong>versi</strong>on;<br />

BANNER<br />

----------------------------------------------------------------<br />

Oracle Database 10g Enterprise Edition Release 10.2.0.2.0 - 64bi<br />

PL/SQL Release 10.2.0.2.0 - Production<br />

CORE 10.2.0.2.0 Production<br />

TNS <strong>for</strong> HPUX: Version 10.2.0.2.0 - Production<br />

NLSRTL Version 10.2.0.2.0 - Production<br />

SQL> select * from t1;<br />

C_SUP C_RAW<br />

---------- ----------<br />

SUP1 R1<br />

SUP2 R2<br />

SQL> select * from t2;<br />

C_INP C_OUT C_PRC<br />

---------- ---------- ----------<br />

R1 S1 P1<br />

S1 T1 P2<br />

T1 U1 P3<br />

U1 V1 P4<br />

R2 S2 P1<br />

S2 T2 P2<br />

T2 U2 P3<br />

U2 V2 P4<br />

V2 W2 P5<br />

9 rows selected.<br />

SQL> l<br />

1 select c_sup, c_out<br />

2 from<br />

3 (<br />

4 select <strong>connect</strong>_<strong>by</strong>_root c_inp root_val, c_out from t2<br />

5 start with c_inp in (select c_raw from t1)<br />

6 <strong>connect</strong> <strong>by</strong> prior c_out = c_inp<br />

7 )t, t1<br />

8* where t.root_val = t1.c_raw<br />

SQL> /<br />

C_SUP C_OUT<br />

---------- ----------<br />

SUP1 V1<br />

SUP1 U1<br />

SUP1 T1<br />

SUP1 S1<br />

SUP2 V2<br />

SUP2 U2<br />

SUP2 T2<br />

SUP2 W2<br />

SUP2 S2<br />

9 rows selected.<br />

Explain plan <strong>for</strong> <strong>the</strong> same.<br />

SQL> /<br />

PLAN_TABLE_OUTPUT<br />

----------------------------------------------------------------------------------------------------


Plan hash value: 2408931310<br />

------------------------------------------------------------------------------------<br />

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |<br />

------------------------------------------------------------------------------------<br />

| 0 | SELECT STATEMENT | | 9 | 126 | 7 (15)| 00:00:01 |<br />

|* 1 | HASH JOIN | | 9 | 126 | 7 (15)| 00:00:01 |<br />

| 2 | TABLE ACCESS FULL | T1 | 2 | 16 | 3 (0)| 00:00:01 |<br />

| 3 | VIEW | | 9 | 54 | 3 (0)| 00:00:01 |<br />

|* 4 | CONNECT BY WITH FILTERING| | | | | |<br />

|* 5 | FILTER | | | | | |<br />

| 6 | TABLE ACCESS FULL | T2 | 9 | 54 | 3 (0)| 00:00:01 |<br />

|* 7 | TABLE ACCESS FULL | T1 | 1 | 3 | 3 (0)| 00:00:01 |<br />

|* 8 | HASH JOIN | | | | | |<br />

| 9 | CONNECT BY PUMP | | | | | |<br />

| 10 | TABLE ACCESS FULL | T2 | 9 | 54 | 3 (0)| 00:00:01 |<br />

| 11 | TABLE ACCESS FULL | T2 | 9 | 54 | 3 (0)| 00:00:01 |<br />

------------------------------------------------------------------------------------<br />

Predicate In<strong>for</strong>mation (identified <strong>by</strong> operation id):<br />

---------------------------------------------------<br />

1 - access("T"."ROOT_VAL"="T1"."C_RAW")<br />

4 - access("C_INP"=PRIOR "C_OUT")<br />

5 - filter( EXISTS (SELECT 0 FROM "T1" "T1" WHERE "C_RAW"=:B1))<br />

7 - filter("C_RAW"=:B1)<br />

8 - access("C_INP"=PRIOR "C_OUT")<br />

27 rows selected.<br />

Regards<br />

Raj<br />

Followup July 7, 2008 - 7am Central time zone:<br />

big page<br />

no idea who "OP" is - <strong>the</strong> original poster asked about "how does <strong>connect</strong> <strong>by</strong> work".<br />

Remember - <strong>the</strong>re are an infinite number of answers to pretty much every <strong>question</strong>. So <strong>the</strong> answer in general to "why can't<br />

we do it this way" is - well, of course you can - you can do something like this, that or <strong>the</strong> o<strong>the</strong>r way.<br />

Not that I verified your approach - I didn't really piece toge<strong>the</strong>r what you were trying to solve - just saying "<strong>the</strong>re are<br />

thousands of ways to do anything"<br />

July 7, 2008 - 9am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Raj from United Kingdom<br />

Tom,<br />

I thought since I have mentioned (To : Car Elcaro) name in <strong>the</strong> subject of my post I assumed it<br />

would have correlated my reference of OP to be this reviewer, apparently it didn't. So to be more<br />

precise I posted one of <strong>the</strong> solutions which could be used <strong>for</strong> his <strong>question</strong> "Currently, my solution<br />

use a PL/SQL to solve it. And I search a better solution if it's exist e.g. without using PL/SQL."<br />

Hope it makes sense this time.<br />

Regards<br />

Raj<br />

using <strong>connect</strong> <strong>by</strong> to replace self join August 5, 2008 - 10pm Central time zone<br />

Reviewer: A reader from NJ<br />

I am referring to your post (reproduced below) on this page earlier where you suggested to use a<br />

self join:<br />

(Dated May 7, 2006 - 11am US/Eastern:)<br />

Bookmark | Bottom | Top


"query all Employees ( lets say whose sal >=3000 ) and <strong>the</strong>ir managers."<br />

why would you use <strong>connect</strong> <strong>by</strong>?<br />

ops$tkyte@ORA10GR2> select ename, mgr, sal from emp where sal >= 3000;<br />

ENAME MGR SAL<br />

---------- ---------- ----------<br />

SCOTT 7566 3000<br />

KING 5000<br />

FORD 7566 3000<br />

or if you needed <strong>the</strong>ir manager name:<br />

1 select a.ename, b.ename mgr, a.sal<br />

2 from emp a, emp b<br />

3 where a.sal >= 3000<br />

4* and a.mgr = b.empno(+)<br />

ops$tkyte@ORA10GR2> /<br />

ENAME MGR SAL<br />

---------- ---------- ----------<br />

FORD JONES 3000<br />

SCOTT JONES 3000<br />

KING 5000<br />

My Question:<br />

(ORACLE VERSION: 10.2.0.2)<br />

I have a view that does a self join to a table that is 25 million rows. Very similar to what you<br />

suggested above. The self join's only purpose is to get employee's manager name. But <strong>the</strong> self join<br />

results in nested loop outer, causing very high lio. Query on this view is executed hundreds of<br />

times in a sec, causing CPU related issues.<br />

I just want to see if <strong>connect</strong> <strong>by</strong> will give me a better execution plan, but I do not know how to<br />

write a <strong>connect</strong> <strong>by</strong> which will give me this:<br />

empno ename job manager's job<br />

7839 KING PRESIDENT NULL<br />

7566 JONES MANAGER PRESIDENT<br />

7788 SCOTT ANALYST MANAGER<br />

7369 SMITH CLERK ANALYST<br />

..<br />

Can you please help me write one? I tried sys_<strong>connect</strong>_<strong>by</strong>_path, but could not go fur<strong>the</strong>r.<br />

Followup August 6, 2008 - 8am Central time zone:<br />

are you using <strong>the</strong> cbo or rbo and what are <strong>the</strong> estimated cardinalities in <strong>the</strong> plan.<br />

<strong>the</strong> optimizer would not use nested loops unless you did something like hint it or used first rows optimization.<br />

show us <strong>the</strong> plan and explain your optimizer environment.<br />

but wait, if you have a query that returns 25 million rows and is executed "hundreds of times per second", <strong>the</strong>n you must not<br />

be returning 25 million rows, you must be returning a very very very very small subset<br />

so, you need to give us more in<strong>for</strong>mation.<br />

and lose <strong>the</strong> <strong>connect</strong> <strong>by</strong> idea, that doesn't apply, doesn't make sense.<br />

February 12, 2009 - 11pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: A reader<br />

Tom,<br />

Need to take care of symbol changes in our database and calculate <strong>the</strong> average volume <strong>for</strong> last 10<br />

days.<br />

create table pricing<br />

(ticker varchar2(10),<br />

px_volume number,<br />

date_loaded date);


create table ticker_changes<br />

(old_ticker varchar2(10),<br />

new_ticker varchar2(10),<br />

start_date date);<br />

insert into pricing<br />

values<br />

('aapl',100,to_date('08/18/2008','mm/dd/yyyy'));<br />

insert into pricing<br />

values<br />

('aapl',200,to_date('08/19/2008','mm/dd/yyyy'));<br />

insert into pricing<br />

values<br />

('aapl',300,to_date('08/20/2008','mm/dd/yyyy'));<br />

insert into pricing<br />

values<br />

('aapl',400,to_date('08/21/2008','mm/dd/yyyy'));<br />

commit;<br />

on 08/22/2008, aapl changes to abc and this is inserted to ticker_changes table. From 08/22/2008<br />

onwards, pricing table has abc instead of aapl<br />

insert into ticker_changes<br />

values<br />

('aapl','abc',to_data('08/22/2008','mm/dd/yyyy'));<br />

My output should be as shown below <strong>for</strong> 08/21/2008<br />

dt ticker avg10d<br />

08/21/2008 aapl 100<br />

10 days be<strong>for</strong>e 08/21/2008 is 08/11/2008. I need to take <strong>the</strong> px_volume <strong>for</strong> all <strong>the</strong>se days and divide<br />

<strong>by</strong> 10. But I only have data on 08/18,08/19,08/20,08/21. Regardless of if I have data on any of<br />

<strong>the</strong>se 10 days, I have to take <strong>the</strong> sum of <strong>the</strong> px_volume and divide <strong>by</strong> 10. So, my average is<br />

(100+200+300+400)/20=100 <strong>for</strong> 08/21/2008<br />

On 08/22/2008, <strong>the</strong>re is a change to ticker = aapl. aapl has been changed to abc. In pricing table,<br />

I will insert one record on 08/22/2008<br />

insert into pricing<br />

values<br />

('abc',500,to_date('08/22/2008','mm/dd/yyyy'));<br />

dt ticker avg10d<br />

08/22/2008 abc 150<br />

10 days be<strong>for</strong>e 08/22/2008 is 08/12/2008. I need to take <strong>the</strong> px_volume <strong>for</strong> all <strong>the</strong>se days and divide<br />

<strong>by</strong> 10. But I only have data on 08/18,08/19,08/20,08/21,08/22 Regardless of if I have data on any of<br />

<strong>the</strong>se 10 days, I have to take <strong>the</strong> sum of <strong>the</strong> px_volume and divide <strong>by</strong> 10. So, my average is<br />

(100+200+300+400+500)/20=150 <strong>for</strong> 08/22/2008. Here <strong>for</strong> my calculations I need to consider aapl from<br />

08/12/2008 to 08/21/2008 and abc on 08/22/2008. I am assuming this can be done with hierachical<br />

queries. Can you guid me?<br />

On 08/25/2008 abc changes to pqr. I have a record in ticker_changes table<br />

insert into ticker_changes<br />

values<br />

('abc','pqr',to_data('08/25/2008','yyyy/mm/dd'));<br />

insert into pricing<br />

values<br />

('abc',600,to_date('08/23/2008','mm/dd/yyyy'));<br />

insert into pricing<br />

values<br />

('abc',700,to_date('08/24/2008','mm/dd/yyyy'));<br />

insert into pricing<br />

values<br />

('pqr',800,to_date('08/25/2008','mm/dd/yyyy'));


dt ticker avg10d<br />

08/24/2008 abc 150<br />

08/25/2008 pqr 260<br />

10 days be<strong>for</strong>e 08/25/2008 is 08/15/2008. I need to take <strong>the</strong> px_volume <strong>for</strong> all <strong>the</strong>se days and divide<br />

<strong>by</strong> 10.<br />

(100+200+300+400+500+600+700+800)/20=260 <strong>for</strong> 08/25/2008.<br />

Can you tell me how to achieve this?<br />

Followup February 16, 2009 - 10am Central time zone:<br />

read through this thread - almost identical<br />

http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:3170642805938#149217820034632054<br />

6<br />

February 17, 2009 - 12pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Reader<br />

Tom,<br />

I went through <strong>the</strong> o<strong>the</strong>r thread that you pointed out and wrote <strong>the</strong> query -<br />

select nvl(<strong>the</strong>_orig_sym,ticker) <strong>the</strong>_sym,<br />

date_loaded,<br />

px_volume<br />

from pricing p left outer join<br />

(select <strong>the</strong>_orig_sym, new_ticker, start_date sdate,<br />

nvl(lead(start_date) over (partition <strong>by</strong> <strong>the</strong>_orig_sym order <strong>by</strong><br />

start_date)-1,to_date('31-DEC-9999','dd-mon-yyyy')) edate<br />

from (select <strong>connect</strong>_<strong>by</strong>_root old_ticker <strong>the</strong>_orig_sym,<br />

new_ticker,<br />

start_date<br />

from ticker_changes<br />

<strong>connect</strong> <strong>by</strong> prior new_ticker = old_ticker and prior start_date < start_date)<br />

) d<br />

on (p.date_loaded between d.sdate and d.edate and (p.ticker = d.new_ticker) )<br />

where (sdate is null and edate is null) or (date_loaded between sdate and edate)<br />

order <strong>by</strong> /*ticker,*/ date_loaded;<br />

I get extra record when <strong>the</strong> start with <strong>connect</strong> <strong>by</strong> is used <strong>for</strong> <strong>the</strong> ticker_chages table, which is<br />

correct. When I try to join this table d with pricing, I am getting extra record <strong>for</strong> ticker=pqr on<br />

08/25/2008 as <strong>the</strong>re are two records <strong>for</strong> pqr in <strong>the</strong> start with <strong>connect</strong> <strong>by</strong> query (table d in this<br />

case). When I run <strong>the</strong> query on 08/25, I need to get only one record. Can you tell me how to achieve<br />

this?<br />

this is <strong>the</strong> query I used to get <strong>the</strong> total of 10 days.<br />

select *<br />

from(<br />

select --nvl(<strong>the</strong>_orig_sym,ticker) <strong>the</strong>_sym,<br />

ticker,<br />

date_loaded,<br />

px_volume,<br />

sum(px_volume) over (partition <strong>by</strong> nvl(<strong>the</strong>_orig_sym,ticker) order <strong>by</strong> date_loaded desc rows<br />

between current row and 9 following) as sum_10d<br />

from pricing p left outer join<br />

(select <strong>the</strong>_orig_sym, new_ticker, start_date sdate,<br />

nvl(lead(start_date) over (partition <strong>by</strong> <strong>the</strong>_orig_sym order <strong>by</strong><br />

start_date)-1,to_date('31-DEC-9999','dd-mon-yyyy')) edate<br />

from (select <strong>connect</strong>_<strong>by</strong>_root old_ticker <strong>the</strong>_orig_sym,<br />

new_ticker,<br />

start_date<br />

from ticker_changes<br />

<strong>connect</strong> <strong>by</strong> prior new_ticker = old_ticker and prior start_date < start_date)<br />

) d<br />

on (p.date_loaded between d.sdate and d.edate and (p.ticker = d.new_ticker) )<br />

where (sdate is null and edate is null) or (date_loaded between sdate and edate)<br />

order <strong>by</strong> date_loaded )<br />

where date_loaded= to_date('08/25/2008','mm/dd/yyyy')


TICKER DATE_LOADED PX_VOLUME SUM_10D<br />

---------- --------- ---------- ---------pqr<br />

25-AUG-08 800 800<br />

pqr 25-AUG-08 800 3600<br />

I should get<br />

TICKER DATE_LOADED PX_VOLUME SUM_10D<br />

---------- --------- ---------- ---------pqr<br />

25-AUG-08 800 3600<br />

February 17, 2009 - 5pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Reader<br />

Tom,<br />

I was wondering if you could answer <strong>the</strong> above <strong>question</strong>.<br />

Followup February 17, 2009 - 8pm Central time zone:<br />

first - how hard have you tried? do you understand how this works (if not, please get <strong>the</strong>re be<strong>for</strong>e going fur<strong>the</strong>r)<br />

second - make <strong>the</strong> test case really easy <strong>for</strong> me to follow - sort of a long narrative above - and you just say "I need one row,<br />

this one" right above - but don't really describe how you know "that is <strong>the</strong> row" (and perhaps when you do, what is needed<br />

will become obvious...)<br />

so, put it all toge<strong>the</strong>r concisely (<strong>the</strong> test case to load <strong>the</strong> data - not scattered in various reviews, it is really hard <strong>for</strong> me to<br />

read up and down over more than one and figure out what is relevant, what is not - especially when hours/days and many<br />

o<strong>the</strong>r <strong>question</strong>s go in between your entries). But truly describe what needs to happen to <strong>the</strong> data, psuedo code it (like we<br />

used to in <strong>the</strong> olden days, to algorithmically describe it to someone...) even<br />

Reviewer: Reader<br />

Tom,<br />

<strong>Thanks</strong> <strong>for</strong> your reply.<br />

February 18, 2009 - 12am Central time zone Bookmark | Bottom | Top<br />

I get data in pricing table every day. pricing table has data about each ticker traded in <strong>the</strong><br />

market. For each ticker that comes in <strong>the</strong> pricing table, 10d sum has to be calculated and has to be<br />

inserted into o<strong>the</strong>r table.<br />

For example:<br />

--08/18<br />

insert into pricing values ('aapl',100,to_date('08/18/2008','mm/dd/yyyy'));<br />

insert into pricing values ('orcl',100,to_date('08/18/2008','mm/dd/yyyy'));<br />

--08/19<br />

insert into pricing values ('aapl',200,to_date('08/19/2008','mm/dd/yyyy'));<br />

insert into pricing values ('orcl',100,to_date('08/19/2008','mm/dd/yyyy'));<br />

--08/20<br />

insert into pricing values ('aapl',300,to_date('08/20/2008','mm/dd/yyyy'));<br />

insert into pricing values ('kkk',300,to_date('08/20/2008','mm/dd/yyyy'));<br />

--08/21<br />

insert into pricing values ('aapl',400,to_date('08/21/2008','mm/dd/yyyy'));<br />

insert into pricing values ('aapl',100,to_date('08/18/2008','mm/dd/yyyy'));<br />

on 08/18, if I run <strong>the</strong> query, <strong>the</strong> result is


select *<br />

from(<br />

select ticker,<br />

sum(px_volume) over (partition <strong>by</strong> ticker order <strong>by</strong> date_loaded desc rows between current<br />

row and 9 following) as sum_10d,<br />

date_loaded<br />

from pricing)<br />

where date_loaded = to_date('08/18/2008','mm/dd/yyyy')<br />

SQL> /<br />

TICKER SUM_10D DATE_LOAD<br />

---------- ---------- --------aapl<br />

100 18-AUG-08<br />

orcl 100 18-AUG-08<br />

On 08/19/2008, I get only aapl,orcl<br />

select *<br />

from(<br />

select ticker,<br />

sum(px_volume) over (partition <strong>by</strong> ticker order <strong>by</strong> date_loaded desc rows between current<br />

row and 9 following) as sum_10d,<br />

date_loaded<br />

from pricing)<br />

where date_loaded = to_date('08/19/2008','mm/dd/yyyy')<br />

SQL> /<br />

TICKER SUM_10D DATE_LOAD<br />

---------- ---------- ---------<br />

aapl 300 19-AUG-08<br />

orcl 200 19-AUG-08<br />

Similary if I run <strong>the</strong> query on 08/20<br />

On 08/20/2008, if I run <strong>the</strong> query:<br />

select *<br />

from(<br />

select ticker,<br />

sum(px_volume) over (partition <strong>by</strong> ticker order <strong>by</strong> date_loaded desc rows between current<br />

row and 9 following) as sum_10d,<br />

date_loaded<br />

from pricing)<br />

where date_loaded = to_date('08/20/2008','mm/dd/yyyy')<br />

SQL> /<br />

TICKER SUM_10D DATE_LOAD<br />

---------- ---------- --------aapl<br />

600 20-AUG-08<br />

kkk 300 20-AUG-08<br />

I have to run this <strong>for</strong> every day that I have data in pricing table<br />

Apparently, <strong>the</strong> ticker aapl got changed to abc on 08/22. And this data is inserted to<br />

ticker_changes table<br />

insert into ticker_changes values ('aapl','abc',to_data('08/22/2008','mm/dd/yyyy'));<br />

And in <strong>the</strong> pricing table, I will start getting abc instead of aapl.<br />

insert into pricing values ('abc',500,to_date('08/22/2008','mm/dd/yyyy'));<br />

When I run <strong>the</strong> query on 08/22 to get <strong>the</strong> 10d sum, I should, consider aapl, prior to 08/22 and abc<br />

from 08/22 onwards.<br />

The sum should be as shown below -<br />

TICKER SUM_10D DATE_LOAD<br />

---------- ---------- --------abc<br />

1500 20-AUG-08<br />

1500 is got from <strong>the</strong> below rows inserted to pricing table -<br />

aapl 100 18-AUG-08<br />

aapl 200 19-AUG-08<br />

aapl 300 20-AUG-08<br />

aapl 400 21-AUG-08<br />

abc 500 22-AUG-08


For this, I used <strong>the</strong> query that you pointed to in <strong>the</strong> o<strong>the</strong>r thread.<br />

select <strong>the</strong>_orig_sym, new_ticker, start_date sdate,<br />

nvl(lead(start_date) over (partition <strong>by</strong> <strong>the</strong>_orig_sym order <strong>by</strong><br />

start_date)-1,to_date('31-DEC-9999','dd-mon-yyyy')) edate<br />

from (select <strong>connect</strong>_<strong>by</strong>_root old_ticker <strong>the</strong>_orig_sym,<br />

new_ticker,<br />

start_date<br />

from ticker_changes<br />

where start_date /<br />

THE_ORIG_S NEW_TICKER SDATE EDATE<br />

---------- ---------- --------- --------aapl<br />

abc 22-AUG-08 31-DEC-99<br />

On 08/22, when I get <strong>the</strong> sum <strong>for</strong> last 10 days, I have to add <strong>the</strong> following and I should get, 1500<br />

and it should be displayed under <strong>the</strong> ticker abc<br />

Ticker volume date_loaded<br />

aapl 100 18-AUG-08<br />

aapl 200 19-AUG-08<br />

aapl 300 20-AUG-08<br />

aapl 400 21-AUG-08<br />

abc 500 22-AUG-08<br />

Result <strong>for</strong> abc along with o<strong>the</strong>r tickers loaded on 08/22<br />

Ticker sum_10d date_loaded<br />

abc 1500 22-AUG-08<br />

(abc is actully <strong>the</strong> sum of aapl(prior to 08/22) and abc (on 08/22)<br />

--08/23 load to pricing table<br />

insert into pricing values ('abc',500,to_date('08/22/2008','mm/dd/yyyy'));<br />

On 08/23, When I get <strong>the</strong> sum <strong>for</strong> last 10 days, I have to add <strong>the</strong> following and I should be getting,<br />

Ticker volume date_loaded<br />

aapl 100 18-AUG-08<br />

aapl 200 19-AUG-08<br />

aapl 300 20-AUG-08<br />

aapl 400 21-AUG-08<br />

abc 500 22-AUG-08<br />

abc 500 23-AUG-08<br />

Result <strong>for</strong> abc along with o<strong>the</strong>r tickers loaded on 08/23<br />

Ticker sum_10d DATE_LOADED<br />

abc 2000 08/23/2008<br />

Please let me know if I am missing something.<br />

Followup February 18, 2009 - 7am Central time zone:<br />

please re-read my request above.<br />

... so, put it all toge<strong>the</strong>r concisely ... not scattered in various reviews, it is really hard <strong>for</strong> me to read up and down over more<br />

than one and figure out what is relevant.... But truly describe what needs to happen to <strong>the</strong> data, psuedo code it (like we used<br />

to in <strong>the</strong> olden days, to algorithmically describe it to someone...) ....<br />

so, in looking at what you posted, would one have to go to more than one review section to get all of <strong>the</strong> necessary<br />

in<strong>for</strong>mation?<br />

but that aside, if you have:<br />

select <strong>the</strong>_orig_sym, new_ticker, start_date sdate,<br />

nvl(lead(start_date) over (partition <strong>by</strong> <strong>the</strong>_orig_sym order <strong>by</strong><br />

start_date)-1,to_date('31-DEC-9999','dd-mon-yyyy')) edate


start_date)-1,to_date('31-DEC-9999','dd-mon-yyyy')) edate<br />

from (select <strong>connect</strong>_<strong>by</strong>_root old_ticker <strong>the</strong>_orig_sym,<br />

new_ticker,<br />

start_date<br />

from ticker_changes<br />

where start_date /<br />

THE_ORIG_S NEW_TICKER SDATE EDATE<br />

---------- ---------- --------- --------aapl<br />

abc 22-AUG-08 31-DEC-99<br />

you basically have a mapping, <strong>the</strong> one you need, I see a single row. What is <strong>the</strong> issue, above you seemed to be saying "i<br />

get two"<br />

Connect By on DUAL table February 18, 2009 - 6am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Matteo Mitrano from Italy<br />

Hi Tom,<br />

with regard to this present topic, could you please explain to me why I found this behaviour in my environment? Is <strong>the</strong>re's<br />

something wrong with it?<br />

SQL> SELECT * FROM v$<strong>versi</strong>on;<br />

Oracle9i Release 9.2.0.1.0 - Production<br />

PL/SQL Release 9.2.0.1.0 - Production<br />

CORE 9.2.0.1.0 Production<br />

TNS <strong>for</strong> 32-bit Windows: Version 9.2.0.1.0 - Production<br />

NLSRTL Version 9.2.0.1.0 - Production<br />

Query 1:<br />

SELECT LEVEL - 1 AS set_of_days<br />

FROM DUAL<br />

CONNECT BY LEVEL


Followup February 18, 2009 - 3pm Central time zone:<br />

it was a 9i really old issue.<br />

you use an inline view as you did to avoid it.<br />

February 18, 2009 - 11pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Reader<br />

Tom,<br />

Regarding <strong>the</strong> previous <strong>question</strong> about <strong>the</strong> ticker changes:<br />

select mt.ticker<br />

,d.<strong>the</strong>_orig_sym<br />

,nvl(d.<strong>the</strong>_orig_sym,mt.ticker) <strong>the</strong>_sym<br />

,mt.date_loaded<br />

,mt.px_volume<br />

from (select * from pricing where date_loaded


from ticker_changes<br />

where start_date


select mt.ticker<br />

,d.<strong>the</strong>_orig_sym<br />

,nvl(d.<strong>the</strong>_orig_sym,mt.ticker) <strong>the</strong>_sym<br />

,mt.date_loaded<br />

,mt.px_volume<br />

from (select * from pricing where date_loaded


ut I want to display only <strong>the</strong> record with <strong>the</strong> total and not pqr with px_volume=800 on 25th.Since<br />

pqr is present in pricing table on 25th, I need to get <strong>the</strong><br />

sum <strong>for</strong> last 10 days, <strong>by</strong> considering ticker changes if any.<br />

if I run <strong>the</strong> below query, I get two records, one with <strong>the</strong> 10day sum 3600 and also one with <strong>the</strong><br />

px_volume = 800 which was loaded on 25th<br />

select ticker,sum_10d,date_loaded<br />

from ( select mt.ticker<br />

,d.<strong>the</strong>_orig_sym<br />

,nvl(d.<strong>the</strong>_orig_sym,mt.ticker) <strong>the</strong>_sym<br />

,mt.date_loaded<br />

,mt.px_volume<br />

,sum(px_volume) over (partition <strong>by</strong> nvl(<strong>the</strong>_orig_sym,ticker) order <strong>by</strong> date_loaded desc<br />

rows between current row and 9 following) as sum_10d<br />

from (select * from pricing where date_loaded


from (select <strong>connect</strong>_<strong>by</strong>_root old_ticker <strong>the</strong>_orig_sym,<br />

new_ticker,<br />

start_date<br />

from ticker_changes<br />

where start_date


How to acheive this? Hope I am clear enough in explaining this. Can I do a union between two<br />

hirearchical queries, one going down and o<strong>the</strong>r going up? If so, how can I go upwards to bring<br />

parent records?<br />

<strong>Thanks</strong> <strong>for</strong> all your help.<br />

Followup March 3, 2009 - 7am Central time zone:<br />

... between two hirearchical queries, one going down and o<strong>the</strong>r going up? ...<br />

sure, but only because <strong>the</strong>re is no such thing as "up or down", <strong>the</strong>re are just links<br />

ops$tkyte%ORA10GR2> select rpad('*',2*level,'*') || ename nm, empno, mgr from scott.emp<br />

2 start with mgr is null<br />

3 <strong>connect</strong> <strong>by</strong> prior empno = mgr<br />

4 /<br />

NM EMPNO MGR<br />

--------------- ---------- ----------<br />

**KING 7839<br />

****JONES 7566 7839<br />

******SCOTT 7788 7566<br />

********ADAMS 7876 7788<br />

******FORD 7902 7566<br />

********SMITH 7369 7902<br />

****BLAKE 7698 7839<br />

******ALLEN 7499 7698<br />

******WARD 7521 7698<br />

******MARTIN 7654 7698<br />

******TURNER 7844 7698<br />

******JAMES 7900 7698<br />

****CLARK 7782 7839<br />

******MILLER 7934 7782<br />

14 rows selected.<br />

ops$tkyte%ORA10GR2><br />

ops$tkyte%ORA10GR2> select rpad('*',2*level,'*') || ename nm, empno, mgr , 'down'<br />

2 from scott.emp<br />

3 start with ename = 'SCOTT'<br />

4 <strong>connect</strong> <strong>by</strong> prior empno = mgr<br />

5 union all<br />

6 select rpad('*',2*level,'*') || ename nm, empno, mgr , 'up'<br />

7 from scott.emp<br />

8 start with ename = 'SCOTT'<br />

9 <strong>connect</strong> <strong>by</strong> prior mgr = empno<br />

10 /<br />

NM EMPNO MGR 'DOW<br />

--------------- ---------- ---------- ----<br />

**SCOTT 7788 7566 down<br />

****ADAMS 7876 7788 down<br />

**SCOTT 7788 7566 up<br />

****JONES 7566 7839 up<br />

******KING 7839 up<br />

February 26, 2009 - 1pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Reader<br />

create table pricing<br />

(ticker varchar2(10),<br />

px_volume number,<br />

date_loaded date);<br />

create table ticker_changes<br />

(old_ticker varchar2(10),<br />

new_ticker varchar2(10),<br />

start_date date);<br />

--data <strong>for</strong> 08/18<br />

insert into pricing values ('aapl',100,to_date('08/18/2008','mm/dd/yyyy'));


insert into pricing values ('orcl',100,to_date('08/18/2008','mm/dd/yyyy'));<br />

--data <strong>for</strong> 08/19<br />

insert into pricing values ('aapl',200,to_date('08/19/2008','mm/dd/yyyy'));<br />

insert into pricing values ('orcl',100,to_date('08/19/2008','mm/dd/yyyy'));<br />

--data <strong>for</strong> 08/20<br />

insert into pricing values ('aapl',300,to_date('08/20/2008','mm/dd/yyyy'));<br />

insert into pricing values ('kkk',300,to_date('08/20/2008','mm/dd/yyyy'));<br />

--data <strong>for</strong> 08/21<br />

insert into pricing values ('aapl',400,to_date('08/21/2008','mm/dd/yyyy'));<br />

--data <strong>for</strong> 08/22<br />

insert into pricing values ('abc',500,to_date('08/22/2008','mm/dd/yyyy'));<br />

insert into pricing values ('orcl',100,to_date('08/22/2008','mm/dd/yyyy'));<br />

--data <strong>for</strong> 08/23<br />

insert into pricing values ('abc',600,to_date('08/23/2008','mm/dd/yyyy'));<br />

--data <strong>for</strong> 08/24<br />

insert into pricing values ('abc',700,to_date('08/24/2008','mm/dd/yyyy'));<br />

--data <strong>for</strong> 08/25<br />

insert into pricing values ('pqr',800,to_date('08/25/2008','mm/dd/yyyy'));<br />

insert into pricing values ('orcl',100,to_date('08/25/2008','mm/dd/yyyy'));<br />

--ticker changes insert<br />

insert into ticker_changes values ('aapl','abc',to_data('08/22/2008','mm/dd/yyyy'));<br />

insert into ticker_changes values ('abc','pqr',to_data('08/25/2008','yyyy/mm/dd'));<br />

commit;<br />

select * from ticker_changes order <strong>by</strong> start_date;<br />

OLD_TICKER NEW_TICKER START_DATE<br />

---------- ---------- ---------aapl<br />

abc 22-AUG-08<br />

abc pqr 25-AUG-08<br />

pqr aapl 26-AUG-08<br />

select * from pricing order <strong>by</strong> date_loaded;<br />

TICKER PX_VOLUME DATE_LOAD<br />

---------- ---------- --------orcl<br />

100 18-AUG-08<br />

aapl 100 18-AUG-08<br />

aapl 200 19-AUG-08<br />

orcl 100 19-AUG-08<br />

kkk 300 20-AUG-08<br />

aapl 300 20-AUG-08<br />

aapl 400 21-AUG-08<br />

orcl 100 22-AUG-08<br />

abc 500 22-AUG-08<br />

abc 600 23-AUG-08<br />

abc 700 24-AUG-08<br />

orcl 100 25-AUG-08<br />

pqr 800 25-AUG-08<br />

As shown in <strong>the</strong> above data from pricing table, I get tickers everday with <strong>the</strong>re volume. There are<br />

chances that ticker can be changed. This ticker changes in<strong>for</strong>mation is in ticker_changes table as<br />

shown above.<br />

Everyday, I need to use pricing table and caclulate last 10 day sum of volume (including that day<br />

in <strong>the</strong> 10 day) <strong>for</strong> each ticker in pricing table. I need to consider<br />

<strong>the</strong> ticker changes in <strong>the</strong> calculation.<br />

On 08/18/2008, I need to calculate sum <strong>for</strong> aapl, orcl. So <strong>the</strong> 10 day volume is from 08/08 to 08/18.<br />

Since I do not have data from 08/08 to 08/17, I use only <strong>the</strong> data from 08/18.<br />

TICKER TOTAL_VOLUME calculation_date


aapl 100 18-AUG-08<br />

orcl 100 18-AUG-08<br />

On 08/19, I have two tickers in pricing table, aapl and orcl. So <strong>the</strong> 10 day volume is from 08/08 to<br />

08/18. Since I have data from 08/18 and 08/19, I use that to calculate <strong>the</strong> volume.<br />

TICKER TOTAL_VOLUME calculation_date<br />

aapl 300 19-AUG-08<br />

orcl 200 19-AUG-08<br />

This continues everyday. On 08/22 as shown in <strong>the</strong> ticker_chages table "aapl" changed to "abc". For<br />

10 day volume calculation on 08/22, I need to consider aapl prior to 08/22 and abc on 08/22<br />

TICKER TOTAL_VOLUME calculation_date<br />

abc 1500 22-AUG-08 (this is sum of aapl+abc)<br />

orcl 300 22-AUG-08<br />

On 08/25, as shown in <strong>the</strong> ticker_changes table "abc" changed to "pqr", For 10 day volume<br />

calculation on 08/25, I need to consider aapl prior to 08/22,<br />

abc from 08/22 to 08/24 and pqr on 08/25<br />

TICKER TOTAL_VOLUME calculation_date<br />

abc 3600 22-AUG-08 (this is sum of aapl+abc)<br />

orcl 400 22-AUG-08<br />

I used <strong>the</strong> hierarchical query that you posted to get <strong>the</strong> ticker changes and <strong>the</strong> sdate and edate<br />

range.<br />

alter session set nls_date_<strong>for</strong>mat='DD-MON-YYYY';<br />

select <strong>the</strong>_orig_sym, new_sym, effective_dt sdate,<br />

nvl(lead(effective_dt) over (partition <strong>by</strong> <strong>the</strong>_orig_sym order <strong>by</strong><br />

effective_dt)-1,to_date('1-jan-3000','dd-mon-yyyy')) edate<br />

from ( select <strong>connect</strong>_<strong>by</strong>_root old_ticker <strong>the</strong>_orig_sym, new_ticker as new_sym, start_date as<br />

effective_dt<br />

from ticker_changes<br />

where start_date


Followup March 3, 2009 - 7am Central time zone:<br />

... If I use this to join to <strong>the</strong> pricing table, I get two records on 08/25 since I<br />

am joining on ticker_changes.new_sym with pricing.ticker<br />

.....<br />

easiest fix - just keep one of <strong>the</strong>m - distinct or group <strong>by</strong> <strong>the</strong> set be<strong>for</strong>e applying <strong>the</strong> aggregates.<br />

Connect By issue April 3, 2009 - 3am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Chandra from India<br />

Hi Tom,<br />

Please go through <strong>the</strong> below script.<br />

CREATE TABLE MATCHTABLE(<br />

MATCH1 VARCHAR2(10),<br />

MATCH2 VARCHAR2(10)<br />

)<br />

INSERT INTO MATCHTABLE VALUES('A1','A2');<br />

INSERT INTO MATCHTABLE VALUES('A2','A1');<br />

INSERT INTO MATCHTABLE VALUES('A2','A3');<br />

INSERT INTO MATCHTABLE VALUES('A3','A2');<br />

INSERT INTO MATCHTABLE VALUES('A4','A5');<br />

INSERT INTO MATCHTABLE VALUES('A5','A4');<br />

INSERT INTO MATCHTABLE VALUES('A4','A6');<br />

INSERT INTO MATCHTABLE VALUES('A6','A4');<br />

INSERT INTO MATCHTABLE VALUES('A7','A8');<br />

INSERT INTO MATCHTABLE VALUES('A8','A7');<br />

SELECT * FROM MATCHTABLE;<br />

MATCH1 MATCH2<br />

----- -----<br />

A1 A2<br />

A2 A1<br />

A2 A3<br />

A3 A2<br />

A4 A5<br />

A5 A4<br />

A4 A6<br />

A6 A4<br />

A7 A8<br />

A8 A7<br />

In this table, we will always have 2 rows <strong>for</strong> corresponding any 2 matchids. I need <strong>the</strong> output<br />

as below grouped matchids in groups. The problem I am facing is <strong>the</strong>re is no starting point from<br />

where to start <strong>the</strong> hierarchy tree. And if I start matches <strong>for</strong> all distinct matchids, <strong>the</strong> groups<br />

tend to be <strong>the</strong> subset of some o<strong>the</strong>r superset and I need <strong>the</strong> supersets only.<br />

GROUPID MATCHID<br />

---------------------<br />

1 A1<br />

1 A2<br />

1 A3<br />

2 A4<br />

2 A5<br />

2 A6<br />

3 A7<br />

3 A8<br />

<strong>Thanks</strong>.


Followup April 3, 2009 - 7am Central time zone:<br />

you need to explain this better. if you have nothing to "start with", no way to describe what to "start with" - how can I figure out<br />

"what to start with"<br />

give me some logic here - FORGET CONNECT BY - it might not even be <strong>the</strong> proper approach. Describe <strong>the</strong> inputs and <strong>the</strong><br />

DESIRED OUTPUTS using english, in <strong>the</strong> <strong>for</strong>m of a program specification - pretend you are <strong>the</strong> end user (not a<br />

programmer) and you want a programmer to develop code <strong>for</strong> you - what would you as <strong>the</strong> end user say to <strong>the</strong> programmer<br />

to describe what you want.<br />

Connect By Clause April 6, 2009 - 2am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Chandra from India<br />

Sorry...<br />

From <strong>the</strong> user perspective, table MAtchTable contains cols Match1 and Match2 which are <strong>the</strong> match<br />

ids. Any row eg A1, A2 corresponds that A1 and A2 ids have got a match among <strong>the</strong>m. So, I need to<br />

group <strong>the</strong> matchids.<br />

We start picking <strong>the</strong> matches from column Match1- A1, A1 = A2 and <strong>the</strong>n <strong>for</strong> A2, A2 = (A3 and A1) and<br />

<strong>the</strong>n <strong>for</strong> A3, A3 = A2. So <strong>by</strong> avoiding <strong>the</strong> circular loop, A1, A2 and A3 falls in a single group. Note<br />

- Circular loops are to be avoided.<br />

If we start from A2, A2=(A1 and A3) and <strong>the</strong>n <strong>for</strong> A1, A1=A2 and <strong>for</strong> A3, A3=A2. So <strong>the</strong>y are again<br />

grouped as A1, A2 and A3.<br />

Similarly, A4,A5 and A6 share common matches, so are grouped toge<strong>the</strong>r.<br />

And so with A7 and A8.<br />

May 19, 2009 - 10am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Balaji from India<br />

Hi tom,<br />

Can you explain how <strong>the</strong> below mentioned query execute, i am confused with<br />

second <strong>connect</strong> <strong>by</strong> prior condition in query2 and <strong>by</strong> adding this <strong>the</strong> query get executed fast.<br />

what is <strong>the</strong> difference between query1 and query2 in execution<br />

query1:-<br />

========<br />

SELECT lm_parentlimit_lm, lm_credprogram_cp_k, lm_limit_k, LEVEL<br />

FROM ca_lmcreditprogramlimits<br />

WHERE lm_credprogram_cp_k = 1088 AND lm_parentlimit_lm IS NULL<br />

START WITH lm_limit_k = 22<br />

CONNECT BY PRIOR lm_parentlimit_lm = lm_limit_k<br />

query2:-<br />

========<br />

SELECT lm_parentlimit_lm, lm_credprogram_cp_k, lm_limit_k, LEVEL<br />

FROM ca_lmcreditprogramlimits<br />

WHERE lm_credprogram_cp_k = 1088 AND lm_parentlimit_lm IS NULL<br />

START WITH lm_limit_k = 22<br />

CONNECT BY PRIOR lm_parentlimit_lm = lm_limit_k<br />

AND PRIOR lm_credprogram_cp_k = lm_credprogram_cp_k<br />

Followup May 23, 2009 - 12pm Central time zone:<br />

<strong>the</strong>y are as different as night and day<br />

you would actually need to understand <strong>the</strong> constructs you are using here.... do you understand <strong>the</strong> use of <strong>connect</strong> <strong>by</strong>, when<br />

and how <strong>the</strong> where clause is applied? that <strong>the</strong> two queries you present are ENTIRELY AND UTTERLY different - <strong>the</strong>y do not<br />

return <strong>the</strong> same answers?<br />

They each are valid.<br />

But <strong>the</strong>y each answer entirely different queries, different <strong>question</strong>s.<br />

One tells you why <strong>the</strong> sky is blue, <strong>the</strong> o<strong>the</strong>r why grass is green (as way of analogy)


A <strong>connect</strong> <strong>by</strong> with a where clause is similar to :<br />

a) build <strong>the</strong> ENTIRE HIERARCHY<br />

b) <strong>the</strong>n apply <strong>the</strong> where clause<br />

A <strong>connect</strong> <strong>by</strong> - <strong>by</strong> itself is<br />

a) build <strong>the</strong> ENTIRE HIERARCHY<br />

now, your query two, it builds (stops faster) a MUCH MUCH SMALLER hierarchy.<br />

The query one, builds a big hierarchy and <strong>the</strong>n filters it.<br />

you cannot compare <strong>the</strong> above two queries, <strong>the</strong>y are as different as<br />

select * from emp;<br />

select * from dept;<br />

are.<br />

Strange behavior December 22, 2010 - 9am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Mihail Bratu from Romania<br />

Hi Tom,<br />

I discovered some strange behavior around <strong>the</strong> <strong>connect</strong> <strong>by</strong> clause. I'll reproduce this issue into one<br />

small test case.<br />

drop table emp<br />

/<br />

drop table enames<br />

/<br />

create table emp as select * from scott.emp<br />

/<br />

create table enames(ename varchar2(10))<br />

/<br />

insert into enames<br />

select column_value from table(sys.odcivarchar2List('KING','JONES','CLARK'))<br />

/<br />

commit<br />

/<br />

Let's see <strong>the</strong> tree starting from mgr is null and including <strong>the</strong> employee names from <strong>the</strong> enames<br />

table:<br />

column ename_s <strong>for</strong>mat a20<br />

select rpad(' ' ,2*(level-1), ' ')|| e.ename ename_s, prior e.ename ename_prior<br />

from emp e<br />

<strong>connect</strong> <strong>by</strong> prior e.empno = e.mgr<br />

and e.ename in (select ename from enames)<br />

start with e.mgr is null<br />

/<br />

ENAME_S ENAME_PRIO<br />

-------------------- ----------<br />

KING<br />

JONES KING<br />

CLARK KING<br />

Now we'll evolve <strong>the</strong> tree applying <strong>the</strong> condition on <strong>connect</strong> <strong>by</strong> with one level delay using <strong>the</strong> prior<br />

operator.<br />

We expect to obtain <strong>the</strong> above tree with one level extended. Instead of that <strong>the</strong> whole tree is<br />

returned.<br />

select rpad(' ' ,2*(level-1), ' ')|| e.ename ename_s, prior e.ename ename_prior<br />

from emp e<br />

<strong>connect</strong> <strong>by</strong> prior e.empno = e.mgr<br />

and prior e.ename in (select ename from enames)


start with e.mgr is null<br />

/<br />

ENAME_S ENAME_PRIO<br />

-------------------- ----------<br />

KING<br />

JONES KING<br />

SCOTT JONES<br />

ADAMS SCOTT<br />

FORD JONES<br />

SMITH FORD<br />

BLAKE KING<br />

ALLEN BLAKE<br />

WARD BLAKE<br />

MARTIN BLAKE<br />

TURNER BLAKE<br />

JAMES BLAKE<br />

CLARK KING<br />

MILLER CLARK<br />

Now we'll use <strong>the</strong> EXISTS function <strong>for</strong> <strong>the</strong> same target:<br />

select rpad(' ' ,2*(level-1), ' ')|| e.ename ename_s, prior e.ename ename_prior<br />

from emp e<br />

<strong>connect</strong> <strong>by</strong> prior e.empno = e.mgr<br />

and prior case when exists(<br />

select null from enames where ename = e.ename<br />

) <strong>the</strong>n 1 end = 1<br />

start with e.mgr is null<br />

/<br />

ENAME_S ENAME_PRIO<br />

-------------------- ----------<br />

KING<br />

JONES KING<br />

SCOTT JONES<br />

FORD JONES<br />

BLAKE KING<br />

CLARK KING<br />

MILLER CLARK<br />

The result matches <strong>the</strong> expectation!<br />

The <strong>question</strong> is, is this a bug, or I'm missing something? I appreciate your comments.<br />

Thank you<br />

Followup December 22, 2010 - 2pm Central time zone:<br />

In 10gR2 - I reproduce your findings (10.2.0.4)<br />

in 11gR2 - I do NOT reproduce your finds, it does <strong>the</strong> right thing (11.2.0.2)<br />

please contact support <strong>for</strong> this one.<br />

Thank you (11.1.0.6.0) December 23, 2010 - 2am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Mihail Bratu<br />

<strong>connect</strong> <strong>by</strong> prior with nulls?? January 21, 2011 - 5am Central time zone Bookmark | Bottom | Top<br />

Reviewer: A reader<br />

Hi Tom,<br />

In our 2 main DBs we have <strong>the</strong> following code<br />

select sysid,party_link_sysid<br />

from parties<br />

CONNECT BY PRIOR SYSID = PARTY_LINK_SYSID<br />

start with sysid=:a1;<br />

both with <strong>the</strong> same indexes. However <strong>the</strong> data is different<br />

DB1<br />

-------


select count(*) from parties;<br />

COUNT(*)<br />

----------<br />

7377820<br />

select count(party_link_sysid) from parties;<br />

COUNT(PARTY_LINK_SYSID)<br />

-----------------------<br />

1533750<br />

DB2<br />

---------<br />

select count(*) from parties;<br />

COUNT(*)<br />

----------<br />

1259267<br />

select count(party_link_sysid) from parties;<br />

COUNT(PARTY_LINK_SYSID)<br />

-----------------------<br />

34<br />

results from autot <strong>for</strong> DB1<br />

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |<br />

--------------------------------------------------------------------------------------------------<br />

| 0 | SELECT STATEMENT | | 8 | 72 | 1 (0)| 00:00:01 |<br />

|* 1 | CONNECT BY WITH FILTERING | | | | | |<br />

| 2 | TABLE ACCESS BY INDEX ROWID | PARTIES | 1 | 24 | 1 (0)| 00:00:01 |<br />

|* 3 | INDEX UNIQUE SCAN | PARTY_PK | 1 | | 1 (0)| 00:00:01 |<br />

| 4 | NESTED LOOPS | | | | | |<br />

| 5 | CONNECT BY PUMP | | | | | |<br />

| 6 | TABLE ACCESS BY INDEX ROWID| PARTIES | 8 | 72 | 1 (0)| 00:00:01 |<br />

|* 7 | INDEX RANGE SCAN | PARTY_PARTY_FK_I | 2 | | 1 (0)| 00:00:01 |<br />

results from autot <strong>for</strong> DB2<br />

| Id | Operation | Name | Rows | Bytes | Cost |<br />

----------------------------------------------------------------------------------<br />

| 0 | SELECT STATEMENT | | 63036 | 492K| 1 |<br />

| 1 | CONNECT BY WITH FILTERING | | | | |<br />

| 2 | TABLE ACCESS BY INDEX ROWID | PARTIES | 1 | 22 | 1 |<br />

| 3 | INDEX UNIQUE SCAN | PARTY_PK | 1 | | 1 |<br />

| 4 | NESTED LOOPS | | | | |<br />

| 5 | CONNECT BY PUMP | | | | |<br />

| 6 | TABLE ACCESS BY INDEX ROWID| PARTIES | 63036 | 492K| 1 |<br />

| 7 | INDEX RANGE SCAN | PARTY_PARTY_FK_I | 1 | | 1 |<br />

----------------------------------------------------------------------------------<br />

So it seems like <strong>the</strong> DB with 33 values and <strong>the</strong> rest of <strong>the</strong> table is null <strong>for</strong> <strong>the</strong> party_link_sysid<br />

column, gets half <strong>the</strong> table scanned.<br />

Is <strong>the</strong>re a way to get it to scan a smaller amount - like in DB1?<br />

<strong>Thanks</strong><br />

Followup January 24, 2011 - 7am Central time zone:<br />

those are explain plans, <strong>the</strong>y are not showing you what actually happened when you ran <strong>the</strong> query, <strong>the</strong>y are showing a<br />

guess of what might happen.<br />

<strong>the</strong>re are no scans here, only indexed reads.<br />

It looks like your statistics are not up to date somewhere and when <strong>the</strong>y get up to date - you'll see different estimates.


It looks like your statistics are not up to date somewhere and when <strong>the</strong>y get up to date - you'll see different estimates.<br />

Use sql_trace+tkprof to see what ACTUALLY happens row wise.<br />

<strong>connect</strong> <strong>by</strong> prior January 24, 2011 - 8am Central time zone Bookmark | Bottom | Top<br />

Reviewer: A reader<br />

<strong>Thanks</strong> tom - should we ignore <strong>the</strong> Rows column in autot ?<br />

What is it <strong>for</strong>?<br />

I have analayzed all indexes and tables involved - but <strong>the</strong> explain plans come out <strong>the</strong> same:<br />

DB1<br />

------------------<br />

SQL> select table_name,num_rows,last_analyzed from dba_tables<br />

2 where table_name ='PARTIES';<br />

TABLE_NAME NUM_ROWS LAST_ANAL<br />

------------------------------ ---------- ---------<br />

PARTIES 7378389 24-JAN-11<br />

SQL> select index_name, num_rows,last_analyzed from dba_indexes<br />

2 where table_name ='PARTIES';<br />

INDEX_NAME NUM_ROWS LAST_ANAL<br />

------------------------------ ---------- ---------<br />

PARTY_PK 7378389 24-JAN-11<br />

PARTY_OPT1 7378389 24-JAN-11<br />

PARTY_PARTY_FK_I 1533762 24-JAN-11<br />

PARTY_PARTY_FK_I2 245157 24-JAN-11<br />

PARTY_OPT2 7372722 24-JAN-11<br />

--------------------------------------------------------------------------------------<br />

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)<br />

--------------------------------------------------------------------------------------<br />

| 0 | SELECT STATEMENT | | 8 | 72 | 1 (0)<br />

|* 1 | CONNECT BY WITH FILTERING | | | |<br />

| 2 | TABLE ACCESS BY INDEX ROWID | PARTIES | 1 | 24 | 1 (0)<br />

|* 3 | INDEX UNIQUE SCAN | PARTY_PK | 1 | | 1 (0)<br />

| 4 | NESTED LOOPS | | | |<br />

| 5 | CONNECT BY PUMP | | | |<br />

| 6 | TABLE ACCESS BY INDEX ROWID| PARTIES | 8 | 72 | 1 (0)<br />

|* 7 | INDEX RANGE SCAN | PARTY_PARTY_FK_I | 2 | | 1 (0)<br />

--------------------------------------------------------------------------------------<br />

DB2<br />

------------------<br />

SQL> select table_name,num_rows,last_analyzed from dba_tables<br />

2 where table_name ='PARTIES';<br />

TABLE_NAME NUM_ROWS LAST_ANAL<br />

------------------------------ ---------- ---------<br />

PARTIES 1208948 21-JAN-11<br />

SQL> select index_name, num_rows,last_analyzed from dba_indexes<br />

2 where table_name ='PARTIES';<br />

INDEX_NAME NUM_ROWS LAST_ANAL<br />

------------------------------ ---------- ---------<br />

PARTY_PK 1208948 21-JAN-11<br />

PARTY_OPT1 1208948 21-JAN-11<br />

PARTY_PARTY_FK_I 22 21-JAN-11<br />

PARTY_PARTY_FK_I2 6 21-JAN-11<br />

PARTY_OPT2 1203585 21-JAN-11<br />

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|<br />

---------------------------------------------------------------------------------------<br />

| 0 | SELECT STATEMENT | | 60447 | 472K| 1 (0)|<br />

|* 1 | CONNECT BY WITH FILTERING | | | | |<br />

| 2 | TABLE ACCESS BY INDEX ROWID | PARTIES | 1 | 22 | 1 (0)|<br />

|* 3 | INDEX UNIQUE SCAN | PARTY_PK | 1 | | 1 (0)|<br />

| 4 | NESTED LOOPS | | | | |<br />

| 5 | CONNECT BY PUMP | | | | |


| 6 | TABLE ACCESS BY INDEX ROWID| PARTIES | 60447 | 472K| 1 (0)|<br />

|* 7 | INDEX RANGE SCAN | PARTY_PARTY_FK_I | 1 | | 1 (0)|<br />

---------------------------------------------------------------------------------------<br />

Does this difference not matter? Both queries timewise are <strong>the</strong> same - but as soon as this query is<br />

joined to o<strong>the</strong>r tables - <strong>the</strong> DB2 per<strong>for</strong>ms 30 times slower.<br />

Followup February 1, 2011 - 10am Central time zone:<br />

... should we ignore <strong>the</strong> Rows column in auto ...<br />

NO, definitely not - that is <strong>the</strong> most important thing in an explain plan!!!<br />

You want to compare it to ACTUAL row counts from a sql_trace when you suspect an inefficient plan is being generated. If<br />

<strong>the</strong> ACTUALS differ widely from <strong>the</strong> GUESS (in <strong>the</strong> plan) - <strong>the</strong>n that is <strong>the</strong> likely cause of <strong>the</strong> wrong plan being generated<br />

and we need to figure out "why" that is happening in order to correct it.<br />

Please - as stated above - sql_plus+tkprof if you want to see what <strong>the</strong>y are ACTUALLY DOING.<br />

As you can see - <strong>the</strong> data sets are completely and utterly different. One of <strong>the</strong>m seems to return A LOT more data. That<br />

might be a clue as to why <strong>the</strong>y per<strong>for</strong>m differently when used as a source to join to some o<strong>the</strong>r table.<br />

LEVEL, CONNECT BY February 3, 2011 - 3pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Ananth from Richmomn, VA USA<br />

Hi Tom,<br />

we have a table "folders" in content management system.<br />

Columns:<br />

folderid number<br />

levl number<br />

name varchar2<br />

pathids varchar2<br />

primary key is on folderid.<br />

Data:<br />

folderid levl name pathids<br />

0 0 Root :0:<br />

1 1 Test1 :0:1:<br />

2 1 Test2 :0:2:<br />

3 2 Test3 :0:1:3:<br />

4 3 Test4 :0:1:3:4:<br />

i want to have result<br />

folderid levl name path-name<br />

-------------------------------<br />

0 0 Root Root<br />

1 1 Test1 Root,Test1<br />

2 1 Test2 Root,Test2<br />

3 2 Test3 Root,Test1,Test3<br />

..<br />

..<br />

Also, when i tried to run a sample query i get different results<br />

SELECT FOLDERID,LEVL,LEVEL,<br />

ROW_NUMBER() OVER (PARTITION BY SNO ORDER BY SNO) RWN,<br />

PATHIDS<br />

FROM<br />

(<br />

SELECT FOLDERID, LEVL, NAME, PATHIDS<br />

FROM PATH<br />

WHERE FOLDERID IN (1)<br />

)<br />

CONNECT BY LEVEL


i get <strong>the</strong> output as<br />

FOLDERID LEVL LEVEL RWN<br />

---------- ---------- ---------- ----------<br />

1 1 2 1<br />

1 1 1 2<br />

why did <strong>the</strong> level column showed in decreasing order.<br />

if i replace it with FOLDERID in (9) instead of (1)<br />

i get <strong>the</strong> results as<br />

FOLDERID LEVL LEVEL RWN<br />

---------- ---------- ---------- ----------<br />

9 4 1 1<br />

9 4 2 2<br />

9 4 5 3<br />

9 4 4 4<br />

9 4 3 5<br />

am not able to figure it out.<br />

Can you please help me out in <strong>the</strong> above 2 scenario's<br />

Followup February 3, 2011 - 4pm Central time zone:<br />

no create table<br />

no inserts<br />

I did not even look at <strong>the</strong> <strong>question</strong>.<br />

Level, Connect By February 3, 2011 - 5pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Ananth from Richmomn, VA USA<br />

Hi Tom,<br />

Please find below <strong>the</strong> create table and insert scripts <strong>for</strong> <strong>the</strong> same.<br />

CREATE TABLE FOLDER<br />

(<br />

FOLDERID NUMBER NOT NULL PRIMARY KEY,<br />

LEVL NUMBER NOT NULL,<br />

NAME VARCHAR2(200) NOT NULL,<br />

PATHIDS VARCHAR2(200) NOT NULL<br />

);<br />

INSERT INTO FOLDER VALUES (0, 0, 'ROOT', ':0:');<br />

INSERT INTO FOLDER VALUES (1, 1, 'TEST1', ':0:1:');<br />

INSERT INTO FOLDER VALUES (2, 2, 'TEST2', ':0:1:2:');<br />

INSERT INTO FOLDER VALUES (3, 2, 'TEST3', ':0:1:3:');<br />

INSERT INTO FOLDER VALUES (4, 3, 'TEST4', ':0:1:3:4:');<br />

INSERT INTO FOLDER VALUES (5, 2, 'TEST5', ':0:1:5:');<br />

INSERT INTO FOLDER VALUES (6, 2, 'TEST6', ':0:1:6:');<br />

INSERT INTO FOLDER VALUES (7, 3, 'TEST7', ':0:1:6:7:');<br />

INSERT INTO FOLDER VALUES (8, 4, 'TEST8', ':0:1:6:7:8:');<br />

INSERT INTO FOLDER VALUES (9, 4, 'TEST9', ':0:1:6:7:9:');<br />

I need a query which shows output as<br />

FOLDERID LEVL NAME PATH<br />

---------- ---------- ---------- ----------<br />

0 0 ROOT ROOT<br />

1 1 TEST1 ROOT:TEST1<br />

2 2 TEST2 ROOT:TEST2<br />

..<br />

..<br />

..<br />

also, <strong>for</strong> <strong>the</strong> below query, on why do <strong>the</strong> results (column LEVEL) are altered when i add analytic<br />

function. With out analytic function <strong>the</strong> level column shows as expected. (increasing order of<br />

LEVEL).<br />

SELECT<br />

FOLDERID,<br />

LEVL,<br />

LEVEL,<br />

ROW_NUMBER() OVER (PARTITION BY FOLDERID ORDER BY FOLDERID) RWNUM,


ROW_NUMBER() OVER (PARTITION BY FOLDERID ORDER BY FOLDERID) RWNUM,<br />

PATHIDS<br />

FROM<br />

(<br />

SELECT FOLDERID, LEVL, NAME, PATHIDS<br />

FROM FOLDER<br />

WHERE FOLDERID = 9<br />

)<br />

CONNECT BY LEVEL


Followup February 6, 2011 - 11am Central time zone:<br />

that is an impossibly bad way to store a hierarchy - putting it back to toge<strong>the</strong>r is impossible (well, not impossible but so<br />

inefficient as to make it be realistically "not something to do")<br />

We have to substr out <strong>the</strong> parent id (levl does *nothing* <strong>for</strong> us to put <strong>the</strong> data back toge<strong>the</strong>r - nothing) and <strong>the</strong> <strong>connect</strong> with it.<br />

It isn't going to be "fast" on non-trivial data sets:<br />

ops$tkyte%ORA11GR2> select id, rpad('*',2*level,'*')||name nm, pathids, pid,<br />

2 sys_<strong>connect</strong>_<strong>by</strong>_path(name,',') scbp<br />

3 from (<br />

4 select folderid id, name, pathids,<br />

5 substr( pathids, instr( pathids, ':', -1, 3 )+1,<br />

instr(pathids,':',-1,2)-instr(pathids,':',-1,3)-1 ) pid<br />

6 from folder<br />

7 )<br />

8 start with id = 0<br />

9 <strong>connect</strong> <strong>by</strong> prior id = pid<br />

10 /<br />

ID NM PATHIDS PID SCBP<br />

---------- -------------------- -------------------- --------------------<br />

------------------------------<br />

0 **ROOT :0: ,ROOT<br />

1 ****TEST1 :0:1: 0 ,ROOT,TEST1<br />

2 ******TEST2 :0:1:2: 1 ,ROOT,TEST1,TEST2<br />

3 ******TEST3 :0:1:3: 1 ,ROOT,TEST1,TEST3<br />

4 ********TEST4 :0:1:3:4: 3 ,ROOT,TEST1,TEST3,TEST4<br />

5 ******TEST5 :0:1:5: 1 ,ROOT,TEST1,TEST5<br />

6 ******TEST6 :0:1:6: 1 ,ROOT,TEST1,TEST6<br />

7 ********TEST7 :0:1:6:7: 6 ,ROOT,TEST1,TEST6,TEST7<br />

8 **********TEST8 :0:1:6:7:8: 7<br />

,ROOT,TEST1,TEST6,TEST7,TEST8<br />

9 **********TEST9 :0:1:6:7:9: 7<br />

,ROOT,TEST1,TEST6,TEST7,TEST9<br />

10 rows selected.<br />

you should <strong>for</strong>get about storing levl and pathids, just store PID - <strong>the</strong> parent id, <strong>the</strong> o<strong>the</strong>rs are all easily derived from <strong>the</strong><br />

<strong>connect</strong> <strong>by</strong> hierarchy.<br />

Connect BY Prior April 5, 2011 - 2pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Ananth from Richmod, VA USA<br />

Hi Tom,<br />

I have a query <strong>regarding</strong> <strong>the</strong> Hierarchial queries.<br />

Is <strong>the</strong>re any way to differentiate between each hierarchy. i.e i want to logically differentiate<br />

each hierarchy ie. (leaf to root), just as how we logically differentiate regions <strong>by</strong> using group <strong>by</strong><br />

region.<br />

can you give me some ideas or suggestions on how to achieve <strong>the</strong> same.<br />

Regards<br />

Ananth<br />

Followup April 12, 2011 - 12pm Central time zone:<br />

have you looked at sys_<strong>connect</strong>_<strong>by</strong>_path - maybe that'll help you.<br />

I'm not really sure what you mean o<strong>the</strong>rwise, I don't know how to compare group <strong>by</strong> with a <strong>connect</strong> <strong>by</strong>? Do you have an<br />

example?<br />

April 12, 2011 - 5pm Central time zone Bookmark | Bottom | Top<br />

Reviewer: Ananth from Richmond, VA USA<br />

Hi Tom,<br />

I could think of CONNECT_BY_ROOT as one option which can differentiate a hierarchy within.


<strong>for</strong> eg:<br />

in traditional employee table if you go from Bottom - Top approach, you have in a hierarchy Employee-his managermanager's<br />

manager and so on till employee with manager_id null.<br />

So if i say that Leaf to Root as one hierarchy. is it possible to do some aggregate kind of things on each hierarchy.<br />

from Traditional employee table design, i see employee_id being unique, i can do <strong>the</strong> aggregate function on hierarchies<br />

using <strong>connect</strong>_<strong>by</strong>_root employee_id.<br />

In some scenarions where we couldnt differentiate a hierarchy.. how can we do on those..?<br />

Regards<br />

Ananth<br />

Followup April 13, 2011 - 9am Central time zone:<br />

give me an example please - use scott.emp. I don't know what you want to "aggregate" - you know how to get <strong>the</strong> root - so<br />

aggregating is easy, you have something to group <strong>by</strong>. But I suspect you mean something OTHER than aggregation don't<br />

you.<br />

give an EXAMPLE of what you are looking <strong>for</strong> - be specific, explain everything.<br />

SYS_CONNECT_BY_PATH April 14, 2011 - 12am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Ananth from Richmond, VA USA<br />

Hi Tom,<br />

Lets say i have <strong>the</strong> path from Root is stored in a table.<br />

For Ex: in Traditional EMPLOYEES table, i have only columns as<br />

EMPLOYEE_ID, EMPLOYEE_PATH (assuming i dont have hierarchy in<strong>for</strong>mation).<br />

<br />

EMPLOYEE_ID EPATH<br />

----------- --------------<br />

101 :101<br />

108 :101:108<br />

109 :101:108:109<br />

111 :101:108:111<br />

112 :101:108:112<br />

113 :101:108:113<br />

110 :101:108:110<br />

204 :101:204<br />

205 :101:205<br />

206 :101:205:206<br />

203 :101:203<br />

200 :101:200<br />

102 :102<br />

103 :102:103<br />

104 :102:103:104<br />

107 :102:103:107<br />

106 :102:103:106<br />

105 :102:103:105<br />

114 :114<br />

115 :114:115<br />

Question1:<br />

-----------<br />

how do i get <strong>the</strong> below output<br />

EMPLOYEE_ID PATH SERIAL<br />

101 101 1<br />

108 101 1<br />

108 108 2<br />

109 101 1<br />

109 108 2<br />

109 109 3<br />

..<br />

..<br />

..<br />

<br />

Followup April 14, 2011 - 9am Central time zone:


this can be done, but what you have to do first is provide<br />

create table<br />

inserts<br />

<strong>the</strong>n I can supply select<br />

actually, you can supply <strong>the</strong> select, here is <strong>the</strong> technique:<br />

http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:2189860818012#214623130034615810<br />

4<br />

Scripts April 14, 2011 - 10am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Ananth from Richmond, VA USA<br />

Hi Tom,<br />

PFB <strong>the</strong> scripts<br />

create table emp<br />

(<br />

id number,<br />

path varchar2(100)<br />

);<br />

insert into emp values (101,':101:');<br />

insert into emp values (108,':101:108:');<br />

insert into emp values (109,':101:108:109');<br />

insert into emp values (111,':101:108:111:');<br />

insert into emp values (112,':101:108:112:');<br />

insert into emp values (113,':101:108:113:');<br />

insert into emp values (110,':101:108:110:');<br />

insert into emp values (204,':101:204:');<br />

insert into emp values (205,':101:205:');<br />

insert into emp values (206,':101:203:');<br />

insert into emp values (203,':101:200:');<br />

insert into emp values (200,':101:200:');<br />

insert into emp values (102,':102:');<br />

insert into emp values (103,':102:103:');<br />

insert into emp values (104,':102:103:104:');<br />

insert into emp values (107,':102:103:107:');<br />

insert into emp values (106,':102:103:106:');<br />

insert into emp values (105,':102:103:105:');<br />

insert into emp values (114,':114:');<br />

insert into emp values (115,':114:115:');<br />

Followup April 14, 2011 - 10am Central time zone:<br />

tell you what Ananth, give it what we call <strong>the</strong> "old college try" using <strong>the</strong> link I provided first. You should be able to - <strong>the</strong>y are<br />

virtually identical in nature.<br />

And if you cannot - come back and show your work and we'll take it from <strong>the</strong>re.<br />

April 14, 2011 - 10am Central time zone Bookmark | Bottom | Top<br />

Reviewer: Ananth from Richmond, VA USA<br />

Sure Tom, Am actually looking into it.<br />

wil surely let you know my approach if i couldnt be able to make it.<br />

ask why conect <strong>by</strong> didn't work at oracle 11g (11.2.0.1.0) July 22, 2011 - 5am Central time zone<br />

Bookmark | Bottom | Top<br />

Reviewer: Liliek from INdonesia<br />

Hi Tom,<br />

i want to ask you :<br />

i have been migrate from oracle 10.2.0.1.0 ver to 11.2.0.1.0 and this start with.. <strong>connect</strong> <strong>by</strong>..<br />

clause not work. could you help me to solve it, because i have a report run <strong>by</strong> this query <strong>for</strong> one<br />

and half hours!!


and half hours!!<br />

thx tom , i really appreciate <strong>for</strong> your help<br />

Rgds<br />

Liliek<br />

Followup July 22, 2011 - 2pm Central time zone:<br />

umm, <strong>connect</strong> <strong>by</strong> and start with most certainly DO WORK with 11g<br />

you'd need to be a bit more precise in describing your problem<br />

PLESE SOLVE MY DOUBT March 30, 2012 - 4am Central time zone Bookmark | Bottom | Top<br />

Reviewer: HANMATH PRADEEP from INDIA<br />

Hello every1 ……………….. I have a small doubt <strong>regarding</strong> retrieving records from <strong>the</strong> base table….. So<br />

please clarify my doubt ……………………………..<br />

The respective base table name is “PRADEEP”.<br />

SQL>SELECT *FROM PRADEEP;<br />

SNO NAME ADDR<br />

------------------------------------<br />

1 HANMATH PRADEEP HYD<br />

2 HARI PRASAD CHENNAI<br />

3 HARI SHANKER BANGLORE<br />

4 HARINATH NAIDU PAKISTAN<br />

5 HARI PRASAD GUNTUR<br />

6 SURESH PAIDY VIZAG<br />

EXPECTING OUTPUT SHOULD BE IN THE FOLLOWING MANNER:<br />

SNO NAME ADDR<br />

1 HANMATH PRADEEP HYD<br />

2 HARI PRASAD CHENNAI<br />

5 HARI PRASAD GUNTUR<br />

MY REQUIREMENT IS AS FOLLOWS:<br />

(1)……… HERE I WOULD LIKE TO RETRIEVE THE RECORDS BASED ON THE NAME COLUMN ONLY.<br />

(2) I KNOW THIS QUERY I.E., (SELECT *FROM PRADEEP WHERE NAME LIKE ‘H_______P%’ OR NAME LIKE<br />

‘H____P%’;) ……………… I DON’T LIKE TO USE SUCH LIKE STATEMENTS HERE? SO PLZ AVOID IT…<br />

(3)……….. THE IMPORTANT CONDITION IS THAT …. THE RECORDS IN WHICH THE STARTING LETTER IS “”H”” (IN<br />

MIDDLE NAME) & THE STARTING LETTER IS “”P”” (IN LAST NAME) SHOULD ONLY RETRIEVED FROM THE BASE<br />

TABLE.<br />

Followup March 30, 2012 - 7am Central time zone:<br />

every1? plz?<br />

what is this? elementary school?<br />

sigh, i did read it - nothing to be read here. "I don't like to use such like statements". to which I say "sorry, but what would you<br />

like to use <strong>the</strong>n"<br />

or where you looking <strong>for</strong> "name like 'H% P%'" - starts with an H and contains a P preceded <strong>by</strong> a space. but that presumes,<br />

assumes that everyone just has a single word first name and single word last name and nothing else (a ra<strong>the</strong>r naive<br />

assumption) but you give us no details.<br />

(did you know your capslock is apparently stuck?)<br />

March 31, 2012 - 11am Central time zone Bookmark | Bottom | Top<br />

Reviewer: sym from US


Is <strong>the</strong>re any way to fold a hierarchy into a collection?<br />

Suppose I have<br />

create type x as object(val varchar2(100)) not final;<br />

create type x_list as table of x;<br />

create type y under x(children x_list);<br />

create table z (<br />

val varchar2(100),<br />

id int,<br />

parent_id int);<br />

insert into z values('a',1, null);<br />

insert into z values('b',2, 1);<br />

insert into z values('c',3, 2);<br />

Is <strong>the</strong>re a way to extract this into x with <strong>the</strong> following structure<br />

x('a',x_list(x('b',x_list(x('c',NULL)))));<br />

<strong>Thanks</strong><br />

March 31, 2012 - 11am Central time zone Bookmark | Bottom | Top<br />

Reviewer: sym from US<br />

The previous <strong>question</strong>, it is a y object and not x that needs to be constructed. Sorry <strong>for</strong> <strong>the</strong><br />

mistake.<br />

Write a Review<br />

All in<strong>for</strong>mation and materials provided here are provided "as-is"; Oracle disclaims all express and implied warranties, including, <strong>the</strong><br />

implied warranties of merchantability or fitness <strong>for</strong> a particular use. Oracle shall not be liable <strong>for</strong> any damages, including, direct, indirect,<br />

incidental, special or consequential damages <strong>for</strong> loss of profits, revenue, data or data use, incurred <strong>by</strong> you or any third party in <strong>connect</strong>ion<br />

with <strong>the</strong> use of this in<strong>for</strong>mation or <strong>the</strong>se materials.<br />

About Oracle | Legal Notices and Terms of Use | Privacy Statement

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!