Reputation: 64
I have table like below which have data like
| cust | val1 | val2 | val3 | val4 | val5 |
---------------------------------------------------
| 1 | x1 | | x3 | | |
| 2 | | | | x4 | |
| 3 | x1 | | | | |
| 4 | x1 | x2 | x3 | x4 | x5 |
and i want output as
cust val1 val2 val3 val4 val5
1 x1 x3
2 x4
3 x1
4 x1 x2 x3 x4 x5
create table test_nth_nonull (cust varchar2(3),val1 varchar2(3),val2 varchar2(3),val3 varchar2(3), val4 varchar2(3), val5 varchar2(3));
insert into test_nth_nonull values(1,'x1',null,'x3',null,null);
insert into test_nth_nonull values(2,null,null,null,'x4',null);
insert into test_nth_nonull values(3,'x1',null,null,null,null);
insert into test_nth_nonull values(4,'x1','x2','x3','x4','x5');
select* from test_nth_nonull;
please help to achieve this in query
Upvotes: 0
Views: 268
Reputation: 1269873
I would unpivot and pivot the data using a lateral join:
select t.*, x.*
from test_nth_nonull t cross join lateral
(select max(case when seqnum = 1 then val end) as val_1,
max(case when seqnum = 2 then val end) as val_2,
max(case when seqnum = 3 then val end) as val_3,
max(case when seqnum = 4 then val end) as val_4,
max(case when seqnum = 5 then val end) as val_5
from (select val, row_number() over (order by n) as seqnum
from (select 1 as n, t.val1 as val from dual union all
select 2, t.val2 as val from dual union all
select 3, t.val3 as val from dual union all
select 4, t.val4 as val from dual union all
select 5, t.val5 as val from dual
) x
where val is not null
) x
) x;
The advantage of this version over putting the values in a string and processing the string is that this preserves the original data types of the column.
The advantage of doing this over a pivot
/unpivot
over the entire table should be performance. The pivoting is only within each row, which should be faster.
Upvotes: 0
Reputation: 12010
You can utilize pivot
and unpivot
clause. First unpivot
columns to rows to get row numbers (the clause ignores nulls by default), then pivot
"shook-down" values back to rows to their new positions:
with test_nth_nonull (cust,val1,val2,val3,val4,val5) as (
select 1,'x1',null,'x3',null,null from dual union all
select 2,null,null,null,'x4',null from dual union all
select 3,'x1',null,null,null,null from dual union all
select 4,'x1','x2','x3','x4','x5' from dual
), unpivotted as (
select *
from test_nth_nonull
unpivot (val for col in (val1 as 1, val2 as 2, val3 as 3, val4 as 4, val5 as 5))
), numbered as (
select u.cust, row_number() over (partition by u.cust order by u.col) as rn, u.val
from unpivotted u
)
select *
from numbered
pivot (max(val) for rn in (1 as val1, 2 as val2, 3 as val3, 4 as val4, 5 as val5))
Upvotes: 2
Reputation: 15893
Using nvl() and decode() along with cte is gonna give you what you want. nvl(val1,val2) will return val2 if val1 is null other wise it will return val1. decode(val2,val1,null,val2) means if val2 =val1 then null else val2
with cte as (
select cust , nvl(nvl(nvl(nvl(val1 ,val2) ,val3 ), val4 ), val5 )val1,nvl(nvl(nvl(val2 ,val3 ), val4 ), val5 )val2,nvl(nvl(val3, val4 ), val5 )val3,nvl( val4 , val5 )val4,val5
from test_nth_nonull
)
select cust,val1,decode(val2,val1,null,val2)val2,decode(val3,val2,null,val1,null,val3)val3,decode(val4,val3,null,val2,null,val1,null,val4)val4,decode(val5,val4,null,val3,null,val2,null,val1,null,val5) val5 from cte
Upvotes: 1
Reputation: 142743
Here's one option.
This is what you have:
SQL> select* from test_nth_nonull order by cust;
CUST VAL1 VAL2 VAL3 VAL4 VAL5
------ ----- ----- ----- ----- -----
1 x1 x3
2 x4
3 x1
4 x1 x2 x3 x4 x5
This is what you want (read comments within code):
SQL> with
2 temp as
3 -- concatenate all values
4 (select cust, val1 ||';'|| val2||';'|| val3||';'|| val4||';'|| val5 val
5 from test_nth_nonull
6 ),
7 temp2 as
8 -- split concatenated column to rows
9 (select cust,
10 regexp_substr(val, '[^;]+', 1, column_value) val,
11 column_value cv
12 from temp
13 cross join table(cast(multiset(select level from dual
14 connect by level <= 5
15 ) as sys.odcinumberlist))
16 )
17 -- final result
18 select cust,
19 max(case when cv = 1 then val end) v1,
20 max(case when cv = 2 then val end) v2,
21 max(case when cv = 3 then val end) v3,
22 max(case when cv = 4 then val end) v4,
23 max(case when cv = 5 then val end) v5
24 from temp2
25 group by cust
26 order by cust;
CUST V1 V2 V3 V4 V5
------ ---- ---- ---- ---- ----
1 x1 x3
2 x4
3 x1
4 x1 x2 x3 x4 x5
SQL>
Upvotes: 1