Skip to Main Content
  • Questions
  • Find the shortest tree to include all fields to fetch all the rows from table.

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Thirupathi.

Asked: April 05, 2017 - 10:27 am UTC

Last updated: April 11, 2017 - 3:32 pm UTC

Version: PL/SQL Release 11.2.0.4.0 - Production

Viewed 1000+ times

You Asked

Hi,
I help people in oracle sql, PLSQL but at this time I needed help :), quick help will be much appreciated,

e.g.
I have table lets say GET_BAYID_TMP2, in that table below rows are there, my project requirement is to get the shortest length of two poles
including all the rows of that shortest length to integrate with Java UI,

GET_BAYID_TMP2 Data

STRUCTURE1(from_pole) STRUCTURE1(to_pole) LENGTHH BAYID
P001                 P002 52 B3
P002                 P005 50 B4
P005                 P006 48 B6
P006                 P007 49 B7
P007                 P009 51 B8
P015                 P020 48 B1
P001                 P015 50 B2
P004                 P009 55 B10
P002                 P004 50 B9 


  CREATE TABLE  GET_BAYID_TMP2 
   ( STRUCTURE1 VARCHAR2(30 BYTE), 
 STRUCTURE2 VARCHAR2(30 BYTE), 
 LENGTHH NUMBER(4,0), 
 BAYID VARCHAR2(30 BYTE)
   ) ;

REM INSERTING into GET_BAYID_TMP2
SET DEFINE OFF;
Insert into GET_BAYID_TMP2 (STRUCTURE1,STRUCTURE2,LENGTHH,BAYID) values ('P001','P002',52,'B3');
Insert into GET_BAYID_TMP2 (STRUCTURE1,STRUCTURE2,LENGTHH,BAYID) values ('P002','P005',50,'B4');
Insert into GET_BAYID_TMP2 (STRUCTURE1,STRUCTURE2,LENGTHH,BAYID) values ('P005','P006',48,'B6');
Insert into GET_BAYID_TMP2 (STRUCTURE1,STRUCTURE2,LENGTHH,BAYID) values ('P006','P007',49,'B7');
Insert into GET_BAYID_TMP2 (STRUCTURE1,STRUCTURE2,LENGTHH,BAYID) values ('P007','P009',51,'B8');
Insert into GET_BAYID_TMP2 (STRUCTURE1,STRUCTURE2,LENGTHH,BAYID) values ('P015','P020',48,'B1');
Insert into GET_BAYID_TMP2 (STRUCTURE1,STRUCTURE2,LENGTHH,BAYID) values ('P001','P015',50,'B2');
Insert into GET_BAYID_TMP2 (STRUCTURE1,STRUCTURE2,LENGTHH,BAYID) values ('P004','P009',55,'B10');
Insert into GET_BAYID_TMP2 (STRUCTURE1,STRUCTURE2,LENGTHH,BAYID) values ('P002','P004',50,'B9');


So far i a have managed to write SQL to get all the corresponding rows but unable to query on shortest paths data to fetch all rows.

SELECT rownum,level cnt,connect_by_isleaf,level+connect_by_isleaf leaflvl,
STRUCTURE1,STRUCTURE2, LENGTHH,BAYID,LPAD(' ', 2*level-1)||sys_connect_by_path(STRUCTURE2,'/')path
   FROM GET_BAYID_TMP2 
  CONNECT BY nocycle PRIOR STRUCTURE2=STRUCTURE1
 START WITH STRUCTURE1 = 'P001' 
and STRUCTURE2<='P009';


Present OUTPUT which is wrong
ROWNUM CNT CONNECT_BY_ISLEAF LEAFLVL STRUCTURE1 STRUCTURE2 LENGTHH BAYID PATH
1 1 0 1 P001 P002 52 B3  /P002
2 2 0 2 P002 P004 50 B9    /P002/P004
3 3 1 4 P004 P009 55 B10      /P002/P004/P009
4 2 0 2 P002 P005 50 B4    /P002/P005
5 3 0 3 P005 P006 48 B6      /P002/P005/P006
6 4 0 4 P006 P007 49 B7        /P002/P005/P006/P007
7 5 1 6 P007 P009 51 B8          /P002/P005/P006/P007/P009



REQUIRED OUTPUT which is CORRECT, REQUIRED HELP in finding the logic

ROWNUM CNT CONNECT_BY_ISLEAF LEAFLVL STRUCTURE1 STRUCTURE2 LENGTHH BAYID PATH
1 1 0 1 P001 P002 52 B3  /P002
2 2 0 2 P002 P004 50 B9    /P002/P004
3 3 1 4 P004 P009 55 B10      /P002/P004/P009

and Chris said...

Here's one way to do it:

- Walk traverse the tree once, finding all the paths.
- Get the shortest complete tree by selecting the min(level) partitioned by whether the row is a leaf or not
- Only return the rows that are leaves where the min level you calculated above = this level

This gives you the shortest complete paths:

with tree as (
SELECT connect_by_isleaf isleaf, STRUCTURE1,STRUCTURE2, bayid, lengthh,
       LPAD(' ', 2*level-1)||sys_connect_by_path(STRUCTURE2,'/') path,
       min(level) over (partition by connect_by_isleaf) shortest,
       level lvl
FROM GET_BAYID_TMP2 t
CONNECT BY nocycle PRIOR STRUCTURE2=STRUCTURE1
 START WITH STRUCTURE1 = 'P001' 
and STRUCTURE2<='P009'
), shortest as (
  select path p from tree
  where  isleaf =  1 
  and    lvl = shortest
)
  select * from shortest;

P                     
     /P002/P004/P009 


To get the full hierarchy, generate it again. But this time only return rows where structure2 is in this path you've just found:

with tree as (
SELECT connect_by_isleaf isleaf, STRUCTURE1,STRUCTURE2, bayid, lengthh,
       LPAD(' ', 2*level-1)||sys_connect_by_path(STRUCTURE2,'/') path,
       min(level) over (partition by connect_by_isleaf) shortest,
       level lvl
FROM GET_BAYID_TMP2 t
CONNECT BY nocycle PRIOR STRUCTURE2=STRUCTURE1
 START WITH STRUCTURE1 = 'P001' 
and STRUCTURE2<='P009'
), shortest as (
  select path p from tree
  where  isleaf =  1 
  and    lvl = shortest
)
  select 
      STRUCTURE1,STRUCTURE2, lengthh, bayid,
      LPAD(' ', 2*level-1)||sys_connect_by_path(STRUCTURE2,'/') path
  from GET_BAYID_TMP2 t cross join shortest s
  CONNECT BY nocycle PRIOR STRUCTURE2= STRUCTURE1
  and   instr(p, structure2) > 0
START WITH STRUCTURE1 = 'P001' 
and  STRUCTURE2<='P009';

STRUCTURE1  STRUCTURE2  LENGTHH  BAYID  PATH                  
P001        P002        52       B3      /P002                
P002        P004        50       B9        /P002/P004         
P004        P009        55       B10         /P002/P004/P009  


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

More to Explore

Analytics

Analytic SQL got you confused? Check out Connor McDonald's complete video course.