Transpose columns when multiple columns match - sql

I'm attempting to flatten the data I have as I'm getting multiple rows that largely contain the same data, it'll currently comes out looking like this:
col1 col2 col3 col4 col5 col6
---- ---- ---- ---- ---- ----
ABC DEF 123 456 1 24
ABC DEF 123 456 2 48
GHI JKL 789 010 1 6
GHI JKL 789 010 2 12
I want the output to be this:
col1 col2 col3 col4 col5 col6 col7 col8
---- ---- ---- ---- ---- ---- ---- ----
ABC DEF 123 456 24 48
GHI JKL 789 010 6 12
This will be done based off of the value of column 5, if it's "1", the value of column 6 moves to column 7, if it's "2", it moves to column 8. This should only happen when the first columns are all matches as well.

You can use conditional aggregation
select col1,col2,col3,col4,null as col5, null as col6,
max(case when col5=1 then col6 end) as col7,
max(case when col5=2 then col6 end) as col8
from tablename
group by col1,col2,col3,col4

You can try this solution using DECODE:
WITH tbl AS(
select 'ABC' as col1,'DEF' as col2,123 as col3,456 as col4,1 as col5,24 as col6 from dual union all
select 'ABC','DEF',123,456,2,48 from dual union all
select 'GHI','JKL',789,010,1,6 from dual union all
select 'GHI','JKL',789,010,2,12 from dual
)
select col1,
col2,
col3,
col4,
decode(min(col5), 1, null, min(col5)) as col5,
decode(max(col5), 2, null, max(col5)) as col6,
decode(min(col5), 1, min(col6), null) as col7,
decode(max(col5), 2, max(col6), null) as col8
from tbl
group by col1, col2, col3, col4
order by 1,2;
Output:

Related

Convert rows to column in Oracle with static column header

I am trying to convert below data set into columns, any help in SQL for this.
Data Set
col1
col2
A
1
A
2
A
3
A
4
A
5
Converted Data Set
col1
col2
col3
col4
col5
col6
A
1
2
3
4
5
You can PIVOT:
SELECT *
FROM data_set
PIVOT(
MAX(col2) FOR col2 IN (
1 AS col2,
2 AS col3,
3 AS col4,
4 AS col5,
5 AS col6
)
)
Or, use conditional aggregation:
SELECT col1,
MAX(CASE col2 WHEN 1 THEN col2 END) AS col2,
MAX(CASE col2 WHEN 2 THEN col2 END) AS col3,
MAX(CASE col2 WHEN 3 THEN col2 END) AS col4,
MAX(CASE col2 WHEN 4 THEN col2 END) AS col5,
MAX(CASE col2 WHEN 5 THEN col2 END) AS col6
FROM data_set
GROUP BY col1
Which, for your sample data:
CREATE TABLE data_set (col1, col2) AS
SELECT 'A', 1 FROM DUAL UNION ALL
SELECT 'A', 2 FROM DUAL UNION ALL
SELECT 'A', 3 FROM DUAL UNION ALL
SELECT 'A', 4 FROM DUAL UNION ALL
SELECT 'A', 5 FROM DUAL;
Both output:
COL1
COL2
COL3
COL4
COL5
COL6
A
1
2
3
4
5
db<>fiddle here

Can I change column order in SQL table based on a value that appears in different columns?

I have a table that looks like this:
Column1 | Column2 | Column3| Column4
4 | 3 | 2 | 1
2 | 1
3 | 2 | 1
I want to flip the columns so that 1 always start in column 1 and then the rest of the values follow to the right. Like this:
Column1 | Column2 | Column3 | Column4
1 | 2 | 3 | 4
1 | 2
1 | 2 | 3
This is an example table. The real table is a hierarchy of a company so 1 = CEO and 2 = SVP for example. 1 is always the same name but as the number gets higher (lower in chain of command) the more names that are in that level. I'm hoping for an automated solution that looks for 1, makes that the first column and then populates the columns. I am struggling because the value that 1 represents is in different columns so I can't just change the order of the columns.
I was able to accomplish this using VBA but I would prefer to keep it in SQL.
I don't have any useful code that I have tried so far.
You can use Case expression:
WITH CTE1 AS
(SELECT 4 AS COL1, 3 AS COL2 , 2 AS COL3, 1 AS COL4 FROM DUAL
UNION ALL
SELECT 2, 1, NULL, NULL FROM DUAL
UNION ALL
SELECT 3, 2, 1, NULL FROM DUAL
)
SELECT CASE WHEN COL1 <> 1 THEN 1 ELSE COL1 END AS COL1,
CASE WHEN COL2 <> 2 THEN 2 ELSE COL2 END AS COL2,
CASE WHEN COL3 <> 3 THEN 3 ELSE COL3 END AS COL3,
CASE WHEN COL4 <> 4 THEN 4 ELSE COL4 END AS COL4
FROM CTE1;
You can apply some CASEes checking all possibilities, this is assuming NULLs for missing data:
COALESCE(col4,col3,col2,col1) AS c1,
CASE
WHEN col4 IS NOT NULL THEN col3
WHEN col3 IS NOT NULL THEN col2
WHEN col2 IS NOT NULL THEN col1
END AS c2,
CASE
WHEN col4 IS NOT NULL THEN col2
WHEN col3 IS NOT NULL THEN col1
END AS c3,
CASE
WHEN col4 IS NOT NULL THEN col1
END AS c4
You want to sort the values. A generic SQL solution would use:
select max(case when seqnum = 1 then col end) as col1,
max(case when seqnum = 2 then col end) as col2,
max(case when seqnum = 3 then col end) as col3,
max(case when seqnum = 4 then col end) as col4
from (select col1, col2, col3, col4, col,
row_number() over (order by col) as seqnum
from ((select col1 as col, 1 as which, col1, col2, col3, col4 from t) union all
(select col2 as col, 2 as which, col1, col2, col3, col4 from t) union all
(select col3 as col, 3 as which, col1, col2, col3, col4 from t) union all
(select col4 as col, 4 as which, col1, col2, col3, col4 from t)
) t
where col is not null
) t
group by col1, col2, col3, col4;
This would be simpler in a database that supports lateral joins. And a unique id on each row would also help.

Select row from group which satisfies condition A. If not, give me a row that satisfies condition B

I bring forth an interesting problem that has been bothering me for the past few days. Let's say you have the following data structure:
Col1 | Col2 | Col3 | Col4
100 | "Val1" | 0 | 100
100 | "Val2" | 1 | null
100 | "Val 3" | 0 | null
101 | "Val4" | 0 | null
101 | "Val5" | 1 | null
102 | "Val6" | 0 | null
I need that one row where Col4!=null. If all rows' Col4 is null then return me a row where Col3=1, but if both Col4 is null and Col3=0, then return me any one row.
So the result set for the above data will look like,
Col1 | Col2 | Col3 | Col4
100 | "Val1" | 0 | 100
101 | "Val5" | 1 | null
102 | "Val6" | 0 | null
I know this could be done using analytics function, order them by Col1, Col4 and Col3 and use an analytic function to get the first row in each group but we are using our inhouse ORM that doesn't support analytic function.
Please let me know if this can be done using simple SQL (JOIN, Case, etc).
Edit:
There will only be one row per group where Col4 has non-null value and one row per group where col3 is 1. Also, a single row in the group can satisfy both conditions of having Col4 not null and Col3=1.
How about this? Every CONDx CTE solves one condition.
COND1 returns rows whose COL4 is not null
COND2 returns rows whose COL1 doesn't exist in COND1 result set and has NULLs for COL4 (in that case, count of distinct values = 0) and COL3 = 1
COND3 is everything that's left
The final result is union of all those.
SQL> with test (col1, col2, col3, col4) as
2 (select 100, 'val1', 0, 100 from dual union all
3 select 100, 'val2', 1, null from dual union all
4 select 100, 'val3', 0, null from dual union all
5 select 101, 'val4', 0, null from dual union all
6 select 101, 'val5', 1, null from dual union all
7 select 102, 'val6', 0, null from dual
8 ),
9 cond1 as
10 (select col1, col2, col3, col4
11 From test
12 where col4 is not null
13 ),
14 cond2 as
15 (select col1, col2, col3, col4
16 from test t
17 where t.col1 not in (select col1 from cond1)
18 and col1 in (select col1
19 from test
20 group by col1
21 having count(distinct col4) = 0
22 )
23 and col3 = 1
24 ),
25 cond3 as
26 (select col1, col2, col3, col4
27 from test t
28 where t.col1 not in (select col1 from cond1
29 union all
30 select col1 from cond2
31 )
32 )
33 select col1, col2, col3, col4 from cond1
34 union all
35 select col1, col2, col3, col4 from cond2
36 union all
37 select col1, col2, col3, col4 from cond3
38 order by col1;
COL1 COL2 COL3 COL4
---------- ---- ---------- ----------
100 val1 0 100
101 val5 1
102 val6 0
SQL>

How can I get data in single row when multiple columns data have null in some columns?

How can I get data in single row when multiple columns data have null in some columns?
Following is the scenario
col1 col2 col3 col4
----- ------ ---------------
1 NULL NULL NULL
NULL 2 NULL NULL
NULL NULL 3 NULL
NULL NULL NULL 4
I want output like this
col1 col2 col3 col4
----- ------ ---------------
1 2 3 4
You can use aggregate functions as below:
select min(col1) as col1,min(col2) as col2,min(col3) as col3,min(col4) as col4 from t
select max(col1) as col1,max(col2) as col2,max(col3) as col3,max(col4) as col4 from t
select sum(col1) as col1,sum(col2) as col2,sum(col3) as col3,sum(col4) as col4 from t
select avg(col1) as col1,avg(col2) as col2,avg(col3) as col3,avg(col4) as col4 from t
However Min or Max or more meaningful than the Avg and Sum in this scenario.
select max(col1) as col1,
max(col2) as col2,
max(col3) as col3,
max(col4) as col4
from your_table
Try this way.
SELECT DISTINCT
(SELECT TOP 1 Col1 FROM TestTable WHERE Col1 IS NOT NULL) AS 'Column1',
(SELECT TOP 1 Col2 FROM TestTable WHERE Col2 IS NOT NULL) AS 'Column2',
(SELECT TOP 1 Col3 FROM TestTable WHERE Col3 IS NOT NULL) AS 'Column3',
(SELECT TOP 1 Col4 FROM TestTable WHERE Col4 IS NOT NULL) AS 'Column4'
From TestTable
Example 01 
Col1 Col2 Col3 Col4
----- ------ ---------------
1 NULL NULL NULL
NULL 2 NULL NULL
NULL NULL 3 NULL
NULL NULL NULL 4
Result
Column1 Column2 Column3 Column4
-------------------------------
1 2 3 4
Example 02
Col1 Col2 Col3 Col4
----- ------ ---------------
1 NULL NULL NULL
NULL 2 NULL 2
5 NULL 3 NULL
NULL NULL NULL 4
Result
Column1 Column2 Column3 Column4
-------------------------------
1 2 3 2

Grouping multiple rows from a table into column

I have two table as below.
Table 1
+------+------+------+------+
| Col1 | Col2 | Col3 | Col4 |
+------+------+------+------+
| 1 | 1.5 | 1.5 | 2.5 |
| 1 | 2.5 | 3.5 | 1.5 |
+------+------+------+------+
Table 2
+------+--------+
| Col1 | Col2 |
+------+--------+
| 1 | 12345 |
| 1 | 678910 |
+------+--------+
I want the result as below.
+------+------+------+------+-------+--------+
| Col1 | Col2 | Col3 | Col4 | Col5 | Col6 |
+------+------+------+------+-------+--------+
| 1 | 4 | 5 | 4 | 12345 | 678910 |
+------+------+------+------+-------+--------+
Here Col2, Col3 and Col4 is the aggregate of value from Col2,3,4 in Table 1. And rows from Table 2 are transposed to Columns in the result.
I use Oracle 11G and tried the PIVOT option. But I couldn't aggregate values from Column 2,3,4 in Table 1.
Is there any function available in Oracle which provides direct solution without any dirty work around?
Thanks in advance.
Since you will always have only 2 records in second table simple grouping and join will do.
Since I dont have tables I am using CTEs and Inline views
with cte1 as (
select 1 as col1 , 1.5 as col2 , 1.5 as col3, 2.5 as col4 from dual
union all
select 1 , 2.5 , 3.5 , 1.5 fom dual
) ,
cte2 as (
select 1 as col1 , 12345 as col2 fom dual
union all
select 1,678910 fom dual )
select* from(
(select col1,sum(col2) as col2 , sum(col3) as col3,sum(col4) as col4
from cte1 group by col1) as x
inner join
(select col1 ,min(col2) as col5 ,max(col2) as col from cte2
group by col1
) as y
on x.col1=y.col1)
with
mytab1 as (select col1, col2, col3, col4, 0 col5, 0 col6 from tab1),
mytab2 as
(
select
col1, 0 col2, 0 col3, 0 col4, "1_COL2" col5, "2_COL2" col6
from
(
select
row_number() over (partition by col1 order by rowid) rn, col1, col2
from
tab2
)
pivot
(
max(col2) col2
for rn in (1, 2)
)
)
select
col1,
sum(col2) col2,
sum(col3) col3,
sum(col4) col4,
sum(col5) col5,
sum(col6) col6
from
(
select * from mytab1 union all select * from mytab2
)
group by
col1
Hello You can use the below query
with t1 (col1,col2,col3,col4)
as
(
select 1,1.5,1.5,2.5 from dual
union
select 1,2.5,3.5,1.5 from dual
),
t2 (col1,col2)
as
(
select 1,12345 from dual
union
select 1,678910 from dual
)
select * from
(
select col1
,max(decode(col2,12345,12345)) as co5
,max(decode(col2,678910,678910)) as col6
from t2
group by col1
) a
inner join
(
select col1,sum(col2) as col2,sum(col3) as col3,sum(col4) as col4
from t1
group by col1
) b
on a.col1=b.col1
Pivot only the second table. You can then do GROUP BY on the nested UNION ALL between table1 (col5 and col6 are null for subsequent group by) and pivoted table2 (col2, col3, col4 are null for subsequent group by).