Skip to Main Content

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Raminder.

Asked: September 06, 2017 - 8:26 am UTC

Last updated: September 06, 2017 - 2:34 pm UTC

Version: 4.0.3.16

Viewed 1000+ times

You Asked

Hi Tom,
I am new here, so if i ask wrong ques or not following the guidelines, apologizes.

Suppose i have a table like this:

COL1
----
A
B
C
D
E
F
G


I have to transform this columns in a single cell like this A,B,C,D,E,F,G. I know that can be done by listagg function but one of my friend said it can be done through connect by also.

Could you please explain how it can be done by connect by?

I tried to get this through this:

select col2
from    (select  level,substr(sys_connect_by_path( col1, ',' ),2) col2
from    t
where   level=7
connect by level<=7
order   by col1)
where   substr(col2,1,1)='A'
        and
        substr(col2,3,1)='B'
        and
        substr(col2,5,1)='C'
        and
        substr(col2,7,1)='D'
        and
        substr(col2,9,1)='E'
        and
        substr(col2,11,1)='F'
        and
        substr(col2,13,1)='G';


But i dont think its a good practice. It could be done in better way(if possible)

and Chris said...

When asking questions, please provide your example data in the form of:

- create table
- insert into table

This reduces the chances of us making a mistake or incorrect assumption when answering your question.

Anyway:

Doing:

where   level=7
connect by level<=7


Generates all possibilities for each letter in each of the seven positions. That 7 ^ 7 = 823,543 rows!

The substrs then search through those to find the one combination that has all the letters in the right place.

That's a colossal amount of wasted effort.

It's better to generate the one row you want in the first place. You can do this by:

- State the beginning of the tree in the start with clause. That's the 'A'
- Use connect by prior to link the next row in the chain

Because you have consecutive letters, you can do this by converting them to their ASCII representation. And finding the one where the previous is one less than the current. e.g.:

prior ascii(l) = ascii(l) - 1


All you need to do then is return the leaf node at the end. The pseudocolumn connect_by_isleaf returns 1 for those that are (zero otherwise).

Put it all together and you get:

with rws as (
  select chr(rownum+64) l from dual
  connect by level <= 7
), tree as (
  select substr ( sys_connect_by_path ( l , ',' ) , 2 ), 
         connect_by_isleaf lf
  from   rws
  start with l = 'A'
  connect by prior ascii(l) = ascii(l) - 1
)
  select * from tree
  where  lf = 1;

SUBSTR(SYS_CONNECT_BY_PATH(L,','),2)  LF  
A,B,C,D,E,F,G                         1 

Rating

  (1 rating)

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

Comments

Thank you

Raminder Singh, September 06, 2017 - 2:26 pm UTC

That the easiest way i think. Thanks for your time.
I'll keep in mind to provide create and insert statements next time.

I have one more question. Could you please enlighten me with connect_by_isleaf Pseudocolumn.

I know there are lots of explanations for this but they are confusing me more.
Chris Saxon
September 06, 2017 - 2:34 pm UTC

It returns 1 if the current row is a leaf (it has no children) and 0 otherwise:

You've got a tree

A -> B -> C -> D -> E -> F -> G

There are no rows "below" G. So it's a leaf and this returns 1.

http://docs.oracle.com/database/122/SQLRF/Hierarchical-Query-Pseudocolumns.htm#SQLRF50941

More to Explore

SQL

The Oracle documentation contains a complete SQL reference.