Convert rows to column in Oracle with static column header - sql

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

Related

convert lines into single row [duplicate]

This question already has answers here:
Dynamic Pivot in Oracle's SQL
(10 answers)
Closed 10 months ago.
I have data like this
Col1 Col 2
100 1
100 2
100 3
100 4
need output like this
Col1 col2 col3 col4 col5
100 1 2 3 4
need to define as new col names and populate values into these fields.
If you have a fixed maximum number of input rows/output columns you can use:
SELECT *
FROM table_name
PIVOT (
MAX(Col2)
FOR Col2 IN (1 AS col2, 2 AS col3, 3 AS col4, 4 AS col5)
)
Which, for the sample data:
CREATE TABLE table_name (Col1, Col2) AS
SELECT 100, 1 FROM DUAL UNION ALL
SELECT 100, 2 FROM DUAL UNION ALL
SELECT 100, 3 FROM DUAL UNION ALL
SELECT 100, 4 FROM DUAL;
Outputs:
COL1
COL2
COL3
COL4
COL5
100
1
2
3
4
If you can have a maximum of 15 rows then:
SELECT *
FROM (
SELECT t.*, ROW_NUMBER() OVER (PARTITION BY Col1 ORDER BY Col2) AS rn
FROM table_name t
)
PIVOT (
MAX(Col2)
FOR rn IN (
1 AS col2,
2 AS col3,
3 AS col4,
4 AS col5,
5 AS col6,
6 AS col7,
7 AS col8,
8 AS col9,
9 AS col10,
10 AS col11,
11 AS col12,
12 AS col13,
13 AS col14,
14 AS col15,
15 AS col16
)
)
or:
SELECT Col1,
MAX(CASE rn WHEN 1 THEN Col2 END) AS Col2,
MAX(CASE rn WHEN 2 THEN Col2 END) AS Col3,
MAX(CASE rn WHEN 3 THEN Col2 END) AS Col4,
MAX(CASE rn WHEN 4 THEN Col2 END) AS Col5,
MAX(CASE rn WHEN 5 THEN Col2 END) AS Col6,
MAX(CASE rn WHEN 6 THEN Col2 END) AS Col7,
MAX(CASE rn WHEN 7 THEN Col2 END) AS Col8,
MAX(CASE rn WHEN 8 THEN Col2 END) AS Col9,
MAX(CASE rn WHEN 9 THEN Col2 END) AS Col10,
MAX(CASE rn WHEN 10 THEN Col2 END) AS Col11,
MAX(CASE rn WHEN 11 THEN Col2 END) AS Col12,
MAX(CASE rn WHEN 12 THEN Col2 END) AS Col13,
MAX(CASE rn WHEN 13 THEN Col2 END) AS Col14,
MAX(CASE rn WHEN 14 THEN Col2 END) AS Col15,
MAX(CASE rn WHEN 15 THEN Col2 END) AS Col16
FROM (
SELECT t.*, ROW_NUMBER() OVER (PARTITION BY Col1 ORDER BY Col2) AS rn
FROM table_name t
)
GROUP BY Col1
Which both output:
COL1
COL2
COL3
COL4
COL5
COL6
COL7
COL8
COL9
COL10
COL11
COL12
COL13
COL14
COL15
COL16
100
1
2
3
4
NULL
NULL
NULL
NULL
NULL
NULL
NULL
NULL
NULL
NULL
NULL
db<>fiddle here
There is 1 more way, called Conditional aggregation to achieve the same result -
SELECT Col1,
MAX(CASE WHEN Col2 = 1 THEN Col2 END) Col2,
MAX(CASE WHEN Col2 = 2 THEN Col2 END) Col3,
MAX(CASE WHEN Col2 = 3 THEN Col2 END) Col4,
MAX(CASE WHEN Col2 = 4 THEN Col2 END) Col5
FROM table_name
GROUP BY Col1;
If you have 15 values, You have to pass them manually in both the queries and your result will always have 15 columns. If you want to have them dynamically transformed, refer the answer in original comment.

Start calculation after the offset in LEAD

I've a weird scenario to work on. I have data in the way below.
Col1 col2 col3
a b 201921
a b 201923
a b 201924
a b 201925
a b 201927
Col1 and col2 etc are there for a partition and there are so many columns like those. I have a dynamic parameter which will feed the offset in LEAD function.
What LEAD doing is for every row, it will find the next value based on offset. But what I need is a little different. When the first row finds the offset, the next row should skip the offset number of rows.
For example, lead of 201921, 1 is 201923. So, the next calculation should happen from the row which has 201924. And then 201927 and further.
What I wrote currently is
Lead(col3,<dynamic param>,col3) over (partition by col1,col2 order by col3)
Is there such a thing to skip rows and continue from the next? I am a bit curious.
Expected output( for offset 1):
Col1 col2 col3 col4
a b 201921 201923
a b 201923 skip
a b 201924 201925
a b 201925 skip
a b 201927 201927
Expected output( for offset 2):
Col1 col2 col3 col4
a b 201921 201924
a b 201923 skip
a b 201924 skip
a b 201925 201925
a b 201927 201927
You can implement using a query determining COL4 using a CASE statement. This will be the base query. <dynamic param> is what will need to be replaced with your dymanic parameter.
SELECT col1,
col2,
col3,
CASE
WHEN ROW_NUMBER () OVER (PARTITION BY col1, col2 ORDER BY col3) + <dynamic param> >
COUNT (*) OVER (PARTITION BY col1, col2)
THEN
col3
WHEN MOD (ROW_NUMBER () OVER (PARTITION BY col1, col2 ORDER BY col3), <dynamic param> + 1) = 1
THEN
LEAD (col3, <dynamic param>) OVER (PARTITION BY col1, col2 ORDER BY col3)
END AS col4
FROM t;
Here are examples using the samples you provided
SQL> --offset of 1
SQL> WITH
2 t (col1, col2, col3)
3 AS
4 (SELECT 'a', 'b', 201921 FROM DUAL
5 UNION ALL
6 SELECT 'a', 'b', 201923 FROM DUAL
7 UNION ALL
8 SELECT 'a', 'b', 201924 FROM DUAL
9 UNION ALL
10 SELECT 'a', 'b', 201925 FROM DUAL
11 UNION ALL
12 SELECT 'a', 'b', 201927 FROM DUAL)
13 SELECT col1,
14 col2,
15 col3,
16 CASE
17 WHEN ROW_NUMBER () OVER (PARTITION BY col1, col2 ORDER BY col3) + 1 >
18 COUNT (*) OVER (PARTITION BY col1, col2)
19 THEN
20 col3
21 WHEN MOD (ROW_NUMBER () OVER (PARTITION BY col1, col2 ORDER BY col3), 1 + 1) = 1
22 THEN
23 LEAD (col3, 1) OVER (PARTITION BY col1, col2 ORDER BY col3)
24 END AS col4
25 FROM t;
COL1 COL2 COL3 COL4
_______ _______ _________ _________
a b 201921 201923
a b 201923
a b 201924 201925
a b 201925
a b 201927 201927
SQL> --offset of 2
SQL> WITH
2 t (col1, col2, col3)
3 AS
4 (SELECT 'a', 'b', 201921 FROM DUAL
5 UNION ALL
6 SELECT 'a', 'b', 201923 FROM DUAL
7 UNION ALL
8 SELECT 'a', 'b', 201924 FROM DUAL
9 UNION ALL
10 SELECT 'a', 'b', 201925 FROM DUAL
11 UNION ALL
12 SELECT 'a', 'b', 201927 FROM DUAL)
13 SELECT col1,
14 col2,
15 col3,
16 CASE
17 WHEN ROW_NUMBER () OVER (PARTITION BY col1, col2 ORDER BY col3) + 2 >
18 COUNT (*) OVER (PARTITION BY col1, col2)
19 THEN
20 col3
21 WHEN MOD (ROW_NUMBER () OVER (PARTITION BY col1, col2 ORDER BY col3), 2 + 1) = 1
22 THEN
23 LEAD (col3, 2) OVER (PARTITION BY col1, col2 ORDER BY col3)
24 END AS col4
25 FROM t;
COL1 COL2 COL3 COL4
_______ _______ _________ _________
a b 201921 201924
a b 201923
a b 201924
a b 201925 201925
a b 201927 201927
If I'm following your logic, you can use a case expression and analytic row_number() calculation to only populate col4 every offset row; something like (with n as the dynamic value):
select col1, col2, col3,
case when mod(row_number() over (partition by col1, col2 order by col3) + n, n + 1) = 0
then
lead(col3, n) over (partition by col1, col2 order by col3)
end as col4
from your_table
order by col1, col2, col3;
db<>fiddle
But that leaves the value in the last row for the partition null. Based on your second example you seem to actually want the last n rows to always have their own col3 value, which you could determine from lead() being null and then coalescing:
case when
mod(row_number() over (partition by col1, col2 order by col3) + n, n + 1) = 0
or lead(col3, n) over (partition by col1, col2 order by col3) is null
then
coalesce(lead(col3, n) over (partition by col1, col2 order by col3), col3)
end as col4
db<>fiddle
or using additional case branches:
case when
lead(col3, n) over (partition by col1, col2 order by col3) is null
then
col3
when
mod(row_number() over (partition by col1, col2 order by col3) + n, n + 1) = 0
then
lead(col3, n) over (partition by col1, col2 order by col3)
end as col4
db<>fiddle
If col3 can be nullable then you could always set the last n rows in the partition to their col3 answer instead of checking if the lead is null:
case when
row_number() over (partition by col1, col2 order by col3 desc) <= n
then
col3
when
mod(row_number() over (partition by col1, col2 order by col3) + n, n + 1) = 0
then
lead(col3, n) over (partition by col1, col2 order by col3)
end as col4
db<>fiddle

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.

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

Replace Value in Column with Value in Different Column

I have a dataset that looks like this in SQL.
Col1 Col2 Col3
A 4 1
B 5 NULL
C 6 1
D 7 NULL
E 8 NULL
How do I add a new column with the values in Col2 with the values in Col3 if Col3 = 1, or else keep the existing values in Col2.
Final Expected Output:
Col1 Col2 Col3 Col4
A 4 1 1
B 5 NULL 5
C 6 1 1
D 7 NULL 7
E 8 NULL 8
I tried the coalesce function but I don't think that worked:
SELECT
Col1,
Col2,
Col3,
coalesce(Col3, Col2) AS Col4
FROM table1
Your description suggests a case expression :
select . . .
(case when col3 = 1 then col3 else col2 end) as col4
You could also express the above as
select . . .
(case when col3 = 1 then 1 else col2 end) as col4
For the data you provided, coalesce() should also work.
Try this:
SELECT
Col1,
Col2,
Col3,
CASE WHEN Col3 IS NOT NULL THEN Col3 ELSE Col2 END AS Col4
FROM table1
-- this should do what you want
SELECT
Col1,
Col2,
Col3,
CASE WHEN Col3 = 1 THEN Col3 ELSE Col2 END AS NewCOL
FROM table1
Insert into table2
select
Col1,
Col2,
Col3,
(case when col3 = 1 then 1 else col2 end) as col4
FROM table1