Skip to Main Content

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Naga.

Asked: March 10, 2018 - 2:13 am UTC

Last updated: April 30, 2018 - 4:10 pm UTC

Version: 18c

Viewed 1000+ times

You Asked

SQL puzzle
Names table
nameData creation script

create table Names (
name char (10),
primary key (name));

insert into Names values ('Al');
insert into Names values ('Ben');
insert into Names values ('Charlie');
insert into Names values ('David');
insert into Names values ('Ed');
insert into Names values ('Frank');
insert into Names values ('Greg');
insert into Names values ('Howard');
insert into Names values ('Ida');
insert into Names values ('Joe');
insert into Names values ('Ken');
insert into Names values ('Larry');
insert into Names values ('Mike');
commit;
-------
Al
Ben
Charlie
David
Ed
Frank
Greg
Howard
Ida
Joe
Ken
Larry
Mike


Name column of Names table
It displays every 5 lines on one line (the remaining column of the last line is null)
Both rows and columns are output in ascending order of the name column in the Names table.

Output example (Convert two rows to one row)
name1 name2
------- ------
Al Ben
Charlie David
Ed Frank
Greg Howard
Ida Joe
Ken Larry
Mike null

Output example (Converting three lines to one line)
name1 name2 name3
----- ------ -------
Al Ben Charlie
David Ed Frank
Greg Howard Ida
Joe Ken Larry
Mike null null

Output example (Convert 4 rows to 1 row)
name1 name2 name3 name4
----- ---- - ------- ------
Al Ben Charlie David
Ed Frank Greg Howard
Ida Joe Ken Larry
Mike null null null

Output example (Convert five rows to one row)
name1 name2 name3 name4 name5
----- ---- - ------- ----- -----
Al Ben Charlie David Ed
Frank Greg Howard Ida Joe
Ken Larry Mike null null                                            


Note:- Can I get an SQL code from Oracle 18c , from latest version..Am very exiting to get this solution from Oracle 18c...I knew the answer for this in an old style like..10g....if solution will be there from 18c side, please get it in here..Thanks a lot...

and Chris said...

No need for 18c. You can do this in 11g!

To convert the rows into N columns:

- Assign every Nth row a number from 1..N
- Group the rows in batches of N and give each group a new value

You can do this by taking the mod and ceil of it's row_number, e.g.:

  mod(row_number() over (order by name)-1, :cols)+1 rn,
  ceil(row_number() over (order by name)/:cols) cl


With the rows normalized like this, you can paste the values 1..N in the pivot IN clause. The ceil grouping is needed to split out rows with the same column value. Otherwise the implicit group by lumps them all together.

Which gives you:

 var cols number;

exec :cols := 3;

with rws as (
  select n.*,
         mod(row_number() over (order by name)-1, :cols)+1 rn,
         ceil(row_number() over (order by name)/:cols) cl
  from   names n
)
  select * from rws 
  pivot (
    min(name) for rn in (1,2,3,4,5)
  )
  order by cl;

CL   1            2            3            4        5        
   1 Al           Ben          Charlie      <null>   <null>   
   2 David        Ed           Frank        <null>   <null>   
   3 Greg         Howard       Ida          <null>   <null>   
   4 Joe          Ken          Larry        <null>   <null>   
   5 Mike         <null>       <null>       <null>   <null>  
  
exec :cols := 4;

with rws as (
  select n.*,
         mod(row_number() over (order by name)-1, :cols)+1 rn,
         ceil(row_number() over (order by name)/:cols) cl
  from   names n
)
  select * from rws 
  pivot (
    min(name) for rn in (1,2,3,4,5)
  )
  order by cl;

CL   1            2            3            4            5        
   1 Al           Ben          Charlie      David        <null>   
   2 Ed           Frank        Greg         Howard       <null>   
   3 Ida          Joe          Ken          Larry        <null>   
   4 Mike         <null>       <null>       <null>       <null>  
  
exec :cols := 5;

with rws as (
  select n.*,
         mod(row_number() over (order by name)-1, :cols)+1 rn,
         ceil(row_number() over (order by name)/:cols) cl
  from   names n
)
  select * from rws 
  pivot (
    min(name) for rn in (1,2,3,4,5)
  )
  order by cl;

CL   1            2            3            4            5            
   1 Al           Ben          Charlie      David        Ed           
   2 Frank        Greg         Howard       Ida          Joe          
   3 Ken          Larry        Mike         <null>       <null> 


OK, so you have trailing null columns. And the upper limit for the number columns is fixed by however many values you specify in the IN clause. But you can always exclude unneeded columns from your select.

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.