This topic demonstrates how to collect and spread values from multiple rows under a single column, like those in VAL below
GROUP_KEY VAL ---------- ---------- G4 a G4 b G4 c G4 d G7 a G7 b G7 c G7 d G7 e G7 f G7 g
across multiple columns, rows, and groups like this.
GROUP_KEY COL_0 COL_1 COL_2 ---------- ---------- ---------- ---------- G4 a c G4 b d G7 a d g G7 b e G7 c f
Unlike prior topics in this section, here we deal with a base table that has no PIVOT_KEY column. Values will be distributed evenly from top to bottom, then left to right across an arbitrary number of columns (this is sometimes referred to as a "newspaper column" style). Empty cells will be placed as far right as possible (unlike the solution in Distributing Top to Bottom - 1 where empty cells were distributed evenly).
11g PIVOT Solution
break on group_key duplicates skip 1
select
group_key ,
col_0 ,
col_1 ,
col_2
from
( select
group_key ,
pivot_key ,
row_number() over( partition by group_key, pivot_key order by val )
as row_num ,
val
from
( select
group_key ,
trunc
( ( row_number() over( partition by group_key order by val ) - 1 ) /
( trunc
( ( count(*) over
( partition by group_key
order by 1 rows between unbounded preceding and unbounded following
) - 1
) / 3
) + 1
)
) as pivot_key ,
val
from t5
) t5a
) t5b pivot
( max(val)
for pivot_key in
(
0 as col_0 ,
1 as col_1 ,
2 as col_2
)
)
order by group_key, row_num ;
GROUP_KEY COL_0 COL_1 COL_2 ---------- ---------- ---------- ---------- G0 G1 a G2 a b G3 a b c G4 a c G4 b d G5 a c e G5 b d G6 a c e G6 b d f G7 a d g G7 b e G7 c f G8 a d g G8 b e h G8 c f
Note how the results for groups G4 and G7 differ from those produced in the Distributing Top to Bottom - 1 topic.
With two columns instead of three the query would look like this.
select
group_key ,
col_0 ,
col_1
-- this line deleted
from
( select
group_key ,
pivot_key ,
row_number() over( partition by group_key, pivot_key order by val )
as row_num ,
val
from
( select
group_key ,
trunc
( ( row_number() over( partition by group_key order by val ) - 1 ) /
( trunc
( ( count(*) over
( partition by group_key
order by 1 rows between unbounded preceding and unbounded following
) - 1
) / 2 -- use "2" here
) + 1
)
) as pivot_key ,
val
from t5
) t5a
) t5b pivot
( max(val)
for pivot_key in
(
0 as col_0 ,
1 as col_1
-- this line deleted
)
)
order by group_key, row_num ;
GROUP_KEY COL_0 COL_1 ---------- ---------- ---------- G0 G1 a G2 a b G3 a c G3 b G4 a c G4 b d G5 a d G5 b e G5 c G6 a d G6 b e G6 c f G7 a e G7 b f G7 c g G7 d G8 a e G8 b f G8 c g G8 d h
The sort order can be controlled by changing the ORDER BY clauses in the analytic function arguments.
select
group_key ,
col_0 ,
col_1 ,
col_2
from
( select
group_key ,
pivot_key ,
row_number() over( partition by group_key, pivot_key ORDER BY VAL DESC )
as row_num ,
val
from
( select
group_key ,
trunc
( ( row_number() over( partition by group_key ORDER BY VAL DESC ) - 1 ) /
( trunc
( ( count(*) over
( partition by group_key
order by 1 rows between unbounded preceding and unbounded following
) - 1
) / 3
) + 1
)
) as pivot_key ,
val
from t5
) t5a
) t5b pivot
( max(val)
for pivot_key in
(
0 as col_0 ,
1 as col_1 ,
2 as col_2
)
)
order by group_key, row_num ;
GROUP_KEY COL_0 COL_1 COL_2 ---------- ---------- ---------- ---------- G0 G1 a G2 b a G3 c b a G4 d b G4 c a G5 e c a G5 d b G6 f d b G6 e c a G7 g d a G7 f c G7 e b G8 h e b G8 g d a G8 f c
How it Works
The ROW_NUM and PIVOT_KEY values created by the inline views are the key to this solution.
break on pivot_key duplicates skip 1
select
group_key ,
pivot_key ,
row_number() over( partition by group_key, pivot_key order by val )
as row_num ,
val
from
( select
group_key ,
trunc
( ( row_number() over( partition by group_key order by val ) - 1 ) /
( trunc
( ( count(*) over
( partition by group_key
order by 1 rows between unbounded preceding and unbounded following
) - 1
) / 3
) + 1
)
) as pivot_key ,
val
from t5
where group_key = 'G8'
) t5a
order by group_key, pivot_key, row_num ;
GROUP_KEY PIVOT_KEY ROW_NUM VAL ---------- ---------- ---------- ---------- G8 0 1 a G8 0 2 b G8 0 3 c G8 1 1 d G8 1 2 e G8 1 3 f G8 2 1 g G8 2 2 h
Once the PIVOT_KEY and ROW_NUM columns have been created, VAL values are simply distributed and collapsed with a PIVOT clause the same way they were in the Transposing M:1 topic.
Earlier Versions
For those who are not working with Oracle 11g the following query works in Oracle versions from 8i onward.
break on group_key duplicates skip 1
select
group_key ,
max( decode( pivot_key, 0, val, null ) ) col_0 ,
max( decode( pivot_key, 1, val, null ) ) col_1 ,
max( decode( pivot_key, 2, val, null ) ) col_2
from
( select
group_key ,
pivot_key ,
row_number() over( partition by group_key, pivot_key order by val )
as row_num ,
val
from
( select
group_key ,
trunc
( ( row_number() over( partition by group_key order by val ) - 1 ) /
( trunc
( ( count(*) over
( partition by group_key
order by 1 rows between unbounded preceding and unbounded following
) - 1
) / 3
) + 1
)
) as pivot_key ,
val
from t5
) t5a
) t5b
group by group_key, row_num
order by group_key, row_num ;
GROUP_KEY COL_0 COL_1 COL_2 ---------- ---------- ---------- ---------- G0 G1 a G2 a b G3 a b c G4 a c G4 b d G5 a c e G5 b d G6 a c e G6 b d f G7 a d g G7 b e G7 c f G8 a d g G8 b e h G8 c f
