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.
Related
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
I have a DB which has 8 columns, all are integers range 1~99:
Col1 Col2 Col3 Col4 Col5 Col6 Col7 Col8
1 13 24 18 35 7 50 88
13 4 33 90 78 42 26 57
22 18 30 3 57 90 71 8
...
When I perform "select Col1, Col2, Col3, Col5, Col6, Col7, Col8 from MyTable where Col4>10"
I would like the return data is sorted, e.g. the first row should return like this:
1,7,13,24,35,50,88
However, "order by" only work on "Column", is there anyway to preform this in SQL ? Or need a temp table/max() to perform this ? Thanks.
Regds
LAM Chi-fung
Your current design is not appropriate for this requirement.
Consider changing it to something like this:
CREATE TABLE tablename (
id INTEGER, -- corresponds to the rowid of your current table
col_id INTEGER NOT NULL, -- 1-8, corresponds to the number of each of the columns ColX
value INTEGER NOT NULL -- corresponds to the value of each of the columns ColX
);
You can populate it from your current table:
INSERT INTO tablename (id, col_id, value)
SELECT rowid, 1, Col1 FROM MyTable UNION ALL
SELECT rowid, 2, Col2 FROM MyTable UNION ALL
SELECT rowid, 3, Col3 FROM MyTable UNION ALL
SELECT rowid, 4, Col4 FROM MyTable UNION ALL
SELECT rowid, 5, Col5 FROM MyTable UNION ALL
SELECT rowid, 6, Col6 FROM MyTable UNION ALL
SELECT rowid, 7, Col7 FROM MyTable UNION ALL
SELECT rowid, 8, Col8 FROM MyTable
Now you can get the result that you want with GROUP_CONCAT() window function and aggregation:
SELECT result
FROM (
SELECT id, GROUP_CONCAT(value) OVER (PARTITION BY id ORDER BY value) result
FROM tablename
WHERE id IN (SELECT id FROM tablename WHERE col_id = 4 AND value > 10)
)
GROUP BY id
HAVING MAX(LENGTH(result))
See the demo.
Results:
result
1,7,13,18,24,35,50,88
4,13,26,33,42,57,78,90
Fix your data model! You should not be storing values in columns. You should be storing them in rows.
That is, SQL doesn't "sort columns". It deals with data in rows!
You can do what you want by unpivoting the data into rows, calculating the new order, and then reaggregating:
with t as (
select row_number() over () as id,
t.*
from mytable t
where col4 > 10
)
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,
max(case when seqnum = 5 then col end) as col5,
max(case when seqnum = 6 then col end) as col6,
max(case when seqnum = 7 then col end) as col7,
max(case when seqnum = 8 then col end) as col8
from (select row_number() over (partition by id order by col) as seqnum,
x.*
from (select id, col1 as col from t union all
select id, col2 as col from t union all
select id, col3 as col from t union all
select id, col4 as col from t union all
select id, col5 as col from t union all
select id, col6 as col from t union all
select id, col7 as col from t union all
select id, col8 as col from t
) x
) x
group by id;
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.
I have a table with many records. It has the following structure:
Table (col1, col2, col3, col4, col5, col6):
a b c val1 val2 val3
d e f val4 val5 val6
d e f val7 val8 val9
g h i val10 val11 val12
g h i val13 val14 val15
g h i val16 val17 val18
And there are many many more other rows.
As a result I want to have a table:
Table (col1, col2, col3, col4_1, col4_2, col4_3, col5_1, col5_2, col5_3, col6_1, col6_2, col6_3):
a b c val1 val2 val3 null null null null null null
d e f val4 val5 val6 val7 val8 val9 null null null
g h i val10 val11 val12 val13 val14 val15 val16 val17 val18
In words: I have some part of the table which has the same col1, col2, col3 but different col4, col5, col6. Number of rows per the same col1, col2, col3 varies from 1-3 (it is unknown beforehand).
My idea was to use GROUP BY, but how to handle col4, col5, col6 values which can be met in dynamic amount of rows?
How to make that kind of table?
You can use conditional aggregation and row_number():
select col1, col2, col3,
max(case when seqnum = 1 then col4 end) as col4_1,
max(case when seqnum = 1 then col5 end) as col5_1,
max(case when seqnum = 1 then col6 end) as col6_1,
max(case when seqnum = 2 then col4 end) as col4_2,
max(case when seqnum = 2 then col5 end) as col5_2,
max(case when seqnum = 2 then col6 end) as col6_2,
max(case when seqnum = 3 then col4 end) as col4_3,
max(case when seqnum = 3 then col5 end) as col5_3,
max(case when seqnum = 3 then col6 end) as col6_3
from (select t.*, row_number() over (partition by col1, col2, col3 order by col1) as seqnum
from t
) t
group by col1, col2, col3
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