UNPIVOT - DB2 SQL - sql

I have data like below
ROW_ID Col0 Col1 Col2 Col3
1 05/22/2020 123 ABC 1
2 05/12/2020 DEF 1 2
3 06/13/2020 PRR N1 4
I am looking for the output where data will transformed very little and then will be un-pivoted as shown below
ROW_ID COLUMN_NAME VALUE
1 Col0 05/22/2020
1 Drv_Col0 May-2020
1 Col1 123
1 Col2 ABC
1 Col3 1
1 Sum_Col3 3
2 Col0 05/12/2020
2 Drv_Col0 May-2020
2 Col1 DEF
2 Col2 1
2 Col3 2
2 Sum_Col3 3
3 Col0 06/13/2020
3 Drv_Col0 Jun-2020
3 Col1 PRR
3 Col2 N1
3 Col3 4
3 Sum_Col3 4

You can use a lateral join. Assuming that the columns all have the same type:
select t.row_id, v.*
from t cross join lateral
(values ('Col0', col0),
('Drv_Col0', to_char(col0, 'MON-YYYY'),
('Col1', col1),
('Col2', col2),
('Col3', col3),
('Sum_Col3', ???)
) v(column_name, value);
Note: You may need to cast the columns so they are all strings.
The question does not specify how sum_col3 is defined and the definition is not obvious. But some expression can go there.

Related

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.

Counting the number of values per column

Assume the following table
---------------------------------------------
ID Col1 Col2 Col3 Col4 ... ColN
--------------------------------------------
1 bla foo abc
2 foo abc
3 bar
4 baz
5 baz bar cuz
6 123 foo
7 123
8 oof
--------------------------------------------
Is there a way to list to count the number of values per column without having to make a separate query for each column?
So output would be like:
----------
Col Hits
----------
Col1 4
Col2 3
Col3 5
Col4 0
...
ColN 2
----------
Any push into the right direction would be great!
You can unpivot and aggregate:
select v.which, count(v.col)
from t cross apply
(values ('col1', col1), ('col2', col2), . . . ('coln', coln)
) v(which, col)
group by v.which;

Sqlite insert both even and odd rows in one expression

I am using sqlite3 and I have a sqlite table which has somewhat duplicated/overlapping columns. To illustrate:
No Col1 Col2 Col3 Col4
row1 1 1 1 2 2
row2 2 1 1 3 3
row3 3 2 2 4 4
row4 4 2 2 5 5
Col1 and Col2 stores the same information, however, Col3 and Col4 has different information.
I want to condense the rows into one row like this:
No Col1 Col2 Col3 Col4 Col3.2 Col4.2
row1 1 1 1 2 2 3 3
row3 3 2 2 4 4 5 5
I have created a new table with the columns, and was able to select the odd rows.
INSERT INTO [Table] ( No, Col1, Col2, Col3, Col4
)
SELECT No, Col1, Col2, Col3, Col4
FROM [Table]
WHERE ([No] % 2) = 1
ORDER BY [No];
The result table would be something like:
No Col1 Col2 Col3 Col4 Col3.2 Col4.2
row1 1 1 1 2 2 null null
row3 3 2 2 4 4 null null
Now I am not sure how to insert the even values into the new table. Using similar expressions only insert more rows. Is it possible to do this INSERT INTO expression in one sentence? Or how do I update the new table?
Just join the table with itself based on the following condition. It'll even work if the No column has gaps:
SELECT o.No, o.Col1, o.Col2, o.Col3, o.Col4, e.Col3, e.Col4
FROM t AS o
INNER JOIN t AS e ON o.Col1 = e.Col1
AND o.Col2 = e.Col2
AND o.No < e.No
Use pivoting logic with aggregation:
SELECT
MIN(No) AS No,
MAX(CASE WHEN No % 2 = 1 THEN Col1 END) AS Col1,
MAX(CASE WHEN No % 2 = 1 THEN Col2 END) AS Col2,
MAX(CASE WHEN No % 2 = 1 THEN Col3 END) AS Col3,
MAX(CASE WHEN No % 2 = 1 THEN Col4 END) AS Col4,
MAX(CASE WHEN No % 2 = 0 THEN Col1 END) AS Col1_2,
MAX(CASE WHEN No % 2 = 0 THEN Col2 END) AS Col2_2,
MAX(CASE WHEN No % 2 = 0 THEN Col3 END) AS Col3_2,
MAX(CASE WHEN No % 2 = 0 THEN Col4 END) AS Col4_2
FROM yourTable
GROUP BY
(No-1) / 2;
Demo
Another approach, using window functions added in sqlite 3.25:
CREATE TABLE table2(no INTEGER PRIMARY KEY, col1, col2, col3, col4, "col3.2", "col4.2");
INSERT INTO table2
SELECT *
FROM (SELECT no, col1, col2, col3, col4, lead(col3) OVER win, lead(col4) OVER win
FROM table1
WINDOW win AS (ORDER BY no))
WHERE no % 2 = 1;
which gives
SELECT * FROM table2;
no col1 col2 col3 col4 col3.2 col4.2
---------- ---------- ---------- ---------- ---------- ---------- ----------
1 1 1 2 2 3 3
3 2 2 4 4 5 5

How to write the below SQL queries?

Consider 3 columns like below. I need to get columns having same col1 with col2 values as 1 and 2 and col3 have 2 different values for col2 where col1 will be same for col2 and col3
Col1 Col2 Col3
A 1 IND
A 2 IND
A 3 IND
B 1 IND
B 2 PAK
B 3 IND
B 4 IND
C 1 IND
C 2 IND
C 3 PAK
C 4 PAK
D 1 IND
D 2 PAK
E 1 PAK
E 2 SA
The result will be as given below
Col1 Col2 Col3
B 1 IND
B 2 PAK
D 1 IND
D 2 PAK
E 1 PAK
E 2 SA
Here is what I tried for col1 and col2:
select col1 from your_table
where col2 in (1,2)
group by col1
having count(distinct col2) > 1
The rows with 3 and 4 in Col2 can be ignored as per your request so
Self Join on col1
Filter to col2 in (1, 2)
col3 is different
Like this
SELECT
*
FROM
MyTable M1
JOIN
MyTable M2 ON M1.COl1 = M2.COl1
WHERE
M1.Col2 IN (1, 2)
AND
M2.Col2 IN (1, 2)
AND
M2.Col3 <> M1.COl3
One approach to this is aggregation. The following gets the column A values that meet your conditions:
select col1
from t
where col2 in (1, 2)
group by col1
having min(col3) <> max(col3);
If you want to get the original rows, there are multiple ways. Apart from obvious ones (such as in, exists, and join), you can use window functions:
select col1, col2, col3
from (select t.*,
min(col3) over (partition by col1) as mincol3,
max(col3) over (partition by col1) as maxcol3
from t
where col2 in (1, 2)
) t
where mincol3 <> maxcol3;

How to figure out which column/value the COALESCE operator successfully selected?

I have a table that I wish to find the first non-null value from 3 (and only 3) columns for each ID starting with Col1 then to Col2 then to Col3
Note: Col3 is NEVER NULL
ID Col1 Col2 Col3
------------------------------
1 A B X
2 NULL C X
3 NULL NULL X
4 D NULL X
To get the correct column for each value I use the following SQL Select
SELECT ID,
COALESCE(Col1, Col2, Col3) AS Col
FROM MyTable
which returns the following and works just fine
ID Col
-------------
1 A
2 C
3 X
4 D
What I want is a third column returned indicating which column the coalesce was successful on. The following is the result set that I wish to produce:
ID Col Source
-----------------------
1 A Col1
2 C Col2
3 X Col3
4 D Col1
Perhaps this will work?
SELECT ID,
COALESCE(Col1, Col2, Col3) AS Col,
CASE COALESCE(Col1, Col2, Col3)
WHEN Col1 THEN 'Col1'
WHEN Col2 THEN 'Col2'
WHEN Col3 THEN 'Col3'
ELSE 'Unknown'
END AS Source
FROM MyTable