Transforming an SQL table using Unpivot - sql

If I have a table with the following structure
ID NAME A1 A2 A3 B1 B2 B3
X----Y------0---1---2---3---4---5 (dashes are just to push values under headers)
How do I transform it to be the following:
ID NAME LETTER 1 2 3
X----Y----------A------0-1-2
X---Y------------B-----3-4-5

SELECT id, name, 'A' letter, a1 1, a2 2, a3 3 from example_table
UNION
SELECT id, name, 'B' letter, b1 1, b2 2, b3 3 from example_table
I'm not sure that 1,2,3 will work as column names; that may be RDBMS-dependant.

While I don't consider this a feasible answer, it may give you something to think about as it produces the end result in sql server.
SELECT
upvt.id
, upvt.name
, left(label,1) AS letter
, CASE
WHEN left(label,1) = 'A' THEN MIN(value)
WHEN left(label,1) = 'B' THEN MAX(value)
END AS [1]
, CASE
WHEN left(label,1) = 'A' THEN MIN(value1)
WHEN left(label,1) = 'B' THEN MAX(value1)
END AS [2]
, CASE
WHEN left(label,1) = 'A' THEN MIN(value2)
WHEN left(label,1) = 'B' THEN MAX(value2)
END AS [3]
FROM example_table
UNPIVOT (
value
FOR label IN (a1,b1)
) upvt
INNER JOIN (
SELECT
id
, name
, left(label1,1) AS letter
, value1
FROM example_table
UNPIVOT (
value1
FOR label1 IN (a2,b2)
) upvt1 ) as b
ON upvt.id = b.id
AND upvt.name = b.name
INNER JOIN (
SELECT
id
, name
, left(label2,1) AS letter
, value2
FROM example_table
UNPIVOT (
value2
FOR label2 IN (a3,b3)
) upvt2 ) c
ON upvt.id = c.id
AND upvt.name = c.name
group by upvt.id, upvt.name, upvt.label
Not exactly what I would call "production ready".

Related

Columns data into rows in oracle

Data is present in the screenshot as table format. I want to convert into desired format as mentioned below.
Table describe :
Table A
(branch_code, branch_name, branch_state, hol1, hol2, hol3....hol100)
Expected Output
TAMF046 14/01/2021
TAMF046 15/01/2021
TAMF046 26/01/2021
KERF047 26/01/2021
KERF047 11/03/2021
KERF047 02/04/2021
This is exactly what UNPIVOT is for
with t(id, c1, c2, c3) as (
select 1, 'a', 'b', 'c' from dual union all
select 2, 'aa', 'bb', 'cc' from dual
)
select *
from t
unpivot (
val
for col in (
c1 as 'A1',
c2 as 'A2',
c3 as 'A3'
)
)
val is the new column that will contain values from columns c1 c2 c3.
col is the new column that will contain the name of the column from where the val comes from.
A1 A2 A3 are the values you want to fill in the col for each unpivoted column (these aliases can be omitted if you are ok with the original column names).
Use union
Eg:
select Branch_Code, Hol1 from MyTable
union
select Branch_Code, Hol2 from MyTable
etc
WITH HOLIDAY
AS (
SELECT BRANCH_CODE,HOL1,HOL2,HOL3,HOL4,HOL5,HOL6,HOL7,HOL8,HOL9,HOL10,HOL11,HOL12,HOL13,HOL14,HOL15,HOL16,HOL17,HOL18,HOL19,HOL20,HOL21,HOL22,HOL23,HOL24,HOL25,HOL26,HOL27,HOL28,HOL29,HOL30,HOL31,HOL32,HOL33,HOL34,HOL35,HOL36,HOL37,HOL38,HOL39,HOL40,HOL41,HOL42,HOL43,HOL44,HOL45,HOL46,HOL47,HOL48,HOL49,HOL50,HOL51,HOL52,HOL53,HOL54,HOL55,HOL56,HOL57,HOL58,HOL59,HOL60,HOL61,HOL62,HOL63,HOL64,HOL65,HOL66,HOL67,HOL68,HOL69,HOL70,HOL71,HOL72,HOL73,HOL74,HOL75,HOL76,HOL77,HOL78,HOL79,HOL80,HOL81,HOL82,HOL83,HOL84,HOL85,HOL86,HOL87,HOL88,HOL89,HOL90,HOL91,HOL92,HOL93,HOL94,HOL95,HOL96,HOL97,HOL98,HOL99,HOL100
FROM CUST_HOLIDAY_MASTER
WHERE BRANCH_CODE = I.BRANCH_CODE
)
SELECT BRANCH_CODE , COLVALUE FROM abc
unpivot
( colvalue for col in (HOL1,HOL2,HOL3,HOL4,HOL5,HOL6,HOL7,HOL8,HOL9,HOL10,HOL11,HOL12,HOL13,HOL14,HOL15,HOL16,HOL17,HOL18,HOL19,HOL20,HOL21,HOL22,HOL23,HOL24,HOL25,HOL26,HOL27,HOL28,HOL29,HOL30,HOL31,HOL32,HOL33,HOL34,HOL35,HOL36,HOL37,HOL38,HOL39,HOL40,HOL41,HOL42,HOL43,HOL44,HOL45,HOL46,HOL47,HOL48,HOL49,HOL50,HOL51,HOL52,HOL53,HOL54,HOL55,HOL56,HOL57,HOL58,HOL59,HOL60,HOL61,HOL62,HOL63,HOL64,HOL65,HOL66,HOL67,HOL68,HOL69,HOL70,HOL71,HOL72,HOL73,HOL74,HOL75,HOL76,HOL77,HOL78,HOL79,HOL80,HOL81,HOL82,HOL83,HOL84,HOL85,HOL86,HOL87,HOL88,HOL89,HOL90,HOL91,HOL92,HOL93,HOL94,HOL95,HOL96,HOL97,HOL98,HOL99,HOL100)
)
WHERE COLVALUE IS NOT NULL;
This is the way which i try to this but if anyone has any other way using dynamic query. Please share your views,

1 of these 2 request apparently equivalent is not working

I try to understand how PIVOT table works
These 2 requests with pivot table seem equivalent:
I only write
tablename.column1, ...........column2 instead of tablename.*
You can find the requests here:
https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=a5c3aacdaebe599bb050295caf3512b6
with
a as
(
select
a1.column_value a, a2.column_value b , cos(a1.r) c
from
(select column_value, rownum r from table(sys.odcinumberlist(1,2,3,4,5))) a1 ,
(select column_value, rownum r from table(sys.odcivarchar2list('a','b','a','b','a'))) a2
where
a1.r = a2.r)
select a.a,a.b,a.c from a --a.a,a.b
PIVOT
(
count(a.a)--,sum(a.c)
FOR b IN ('a', 'b')
)
ORA-00904: "A"."C": invalid identifier
with
a as
(
select
a1.column_value a, a2.column_value b , cos(a1.r) c
from
(select column_value, rownum r from table(sys.odcinumberlist(1,2,3,4,5))) a1 ,
(select column_value, rownum r from table(sys.odcivarchar2list('a','b','a','b','a'))) a2
where
a1.r = a2.r)
select * from a
PIVOT
(
COUNT(a.a)--,sum(a.c)
FOR b IN ('a', 'b')
)
intended result
When you do a PIVOT, Oracle will name the resulting columns just like their original values.
You can see this behavior when you do your select * that is working :
with
a as
(
select
a1.column_value a, a2.column_value b , cos(a1.r) c
from
(select column_value, rownum r from table(sys.odcinumberlist(1,2,3,4,5))) a1 ,
(select column_value, rownum r from table(sys.odcivarchar2list('a','b','a','b','a'))) a2
where
a1.r = a2.r)
select * from a
PIVOT
(
COUNT(a.a)--,sum(a.c)
FOR b IN ('a', 'b')
)
result is
C 'a' 'b'
-.65364362086361191463916818309775038145 0 1
.5403023058681397174009366074429766037354 1 0
-.98999249660044545727157279473126130238 1 0
.2836621854632262644666391715135573083265 1 0
-.41614683654714238699756822950076218977 0 1
Your columns headings have been turned by Oracle into the exact values you've got in the IN clause, including the surrounding quotes.
So to refer them in your SELECT clause, you should use double quotes like this:
select "'a'","'b'", c from a
PIVOT
(
count(a.a)--,sum(a.c)
FOR b IN ('a', 'b')
)
An alternative is to alias your values directly in the IN clause
select val_a, val_b, c from a --a.a,a.b
PIVOT
(
count(a.a)--,sum(a.c)
FOR b IN ('a' val_a, 'b' val_b )
)
VAL_A VAL_B C
0 1 -.65364362086361191463916818309775038145
1 0 .5403023058681397174009366074429766037354
1 0 -.98999249660044545727157279473126130238
1 0 .2836621854632262644666391715135573083265
0 1 -.41614683654714238699756822950076218977
And finally, you had another mistake in your initial approach:
select a.a,a.b,a.c from a --a.a,a.b
PIVOT
(
count(a.a)--,sum(a.c)
FOR b IN ('a', 'b')
)
in this query a refers to your initial a CTE. When you do a.a,a.b,a.c, Oracle doesn't know what you are referencing because of the PIVOT that comes afterwards.
You should properly alias the PIVOT results if you want to refer to it in the SELECT clause :
select pa."'a'",pa."'b'", pa.c from a
PIVOT
(
count(a.a)--,sum(a.c)
FOR b IN ('a', 'b')
) pa

Any CONCAT() variation that tolerates NULL values?

CONCAT() returns NULL when any value is NULL. I have to use IFNULL() to
wrap all fields passed to CONCAT(). Is there a CONCAT() variation that just
ignores NULL?
For example:
#standardSQL
WITH data AS (
SELECT 'a' a, 'b' b, CAST(null AS STRING) nu
)
SELECT CONCAT(a, b, nu) concatenated, ARRAY_TO_STRING([a,b,nu], ',') w_array_to_string
FROM `data`
--->
null
Quick Jam Session on interesting theme in question
There are potentially unlimited combination of real-life use cases here
Below are few variations:
#standardSQL
WITH data AS (
SELECT 'a' a, 'b' b, 'c' c UNION ALL
SELECT 'y', 'x', NULL UNION ALL
SELECT 'v', NULL, 'w'
)
SELECT
*,
CONCAT(a, b, c) by_concat,
ARRAY_TO_STRING([a,b,c], '') by_array_to_string,
ARRAY_TO_STRING([a,b,c], '', '?') with_null_placeholder,
ARRAY_TO_STRING(
(SELECT ARRAY_AGG(col ORDER BY col DESC)
FROM UNNEST([a,b,c]) AS col
WHERE NOT col IS NULL)
, '') with_order
FROM `data`
The output is:
a b c by_concat by_array_to_string with_null_placeholder with_order
- ---- ---- --------- ------------------ --------------------- ----------
y x null null yx yx? yx
a b c abc abc abc cba
v null w null vw v?w wv
Use ARRAY_TO_STRING([col1, col2, ...]) instead:
#standardSQL
WITH data AS (
SELECT 'a' a, 'b' b, CAST(null AS STRING) nu
)
SELECT ARRAY_TO_STRING([a,b,nu], '') w_array_to_string
FROM `data`
--->
ab

SQL Server - How to delete some rows of columns without disrupting the rest of the record

I have it
-- -- -- --
01 A1 B1 99
01 A1 B1 98
02 A2 B2 97
02 A2 B2 96
I need this
-- -- -- --
01 A1 B1 99
98
02 A2 B2 97
96
------------
I can not repeat the data that I will present in a excel,
My result needs to be just so.
In my actual table, the last column are responses of forms and the first columns (those that can not repeat) are customer data as (phone, name ...).
The end result of this "query" will populate a "DataTable" and will be presented in a file "xlsx".
Thanks for sharing knowledge ^^
If you have SQL2012+
SELECT
ISNULL(NULLIF(Column1,LAG(Column1) OVER(ORDER BY Column1)),'')
,ISNULL(NULLIF(Column2,LAG(Column2) OVER(ORDER BY Column1,Column2)),'')
,ISNULL(NULLIF(Column3,LAG(Column3) OVER(ORDER BY Column1,Column2,Column3)),'')
,Column4
FROM #mytable
ORDER BY Column1,Column2,Column3,Column4 DESC
It's a little messy, but you can do it in the database. You basically make a subquery that gets the smallest value, and then join that to the regular table and blank out values that don't match. I created your sample set like this:
CREATE TABLE mytable (N1 VARCHAR(2), A VARCHAR(2), B VARCHAR(2), N2 VARCHAR(2))
INSERT INTO mytable VALUES
('01', 'A1', 'B1', '99'),
('01', 'A1', 'B1', '98'),
('02', 'A2', 'B2', '97'),
('02', 'A2', 'B2', '96')
And then was able to get the result like this:
SELECT
CASE WHEN O.N2 = I.N2 THEN O.N1 ELSE '' END,
CASE WHEN O.N2 = I.N2 THEN O.A ELSE '' END,
CASE WHEN O.N2 = I.N2 THEN O.B ELSE '' END,
O.N2
FROM
(SELECT MAX(N2) AS N2, N1, A, B FROM mytable GROUP BY N1, A, B) I
INNER JOIN mytable O
ON O.A = I.A AND O.B = I.B AND O.N1 = I.N1
ORDER BY O.N1 ASC
we can use ROW_NUMBER to get the sequence and substitute '' for all rows where sequence is greater than 1
with CTE
AS
( SELECT ID, ColumnA, ColumnB, value,ROW_NUMBER() over ( PARTITION by id order by id) as seq
FROM tableA
)
, CTE1
AS
(
select id, ColumnA, ColumnB, value, seq from CTE where seq =1
UNION
SELECT id ,'','', value , seq from CTE where seq >1
)
SELECT case when seq >1 THEN NULL ELSE id END as id, columnA, columnB, value from CTE1
You can achieve what you want using a query.
You haven't provided DDL so I am going to asume your columns are called a, b, c and d respectively
; WITH cte AS (
SELECT a
, b
, c
, d
, Row_Number() OVER (PARTITION BY a, b, c ORDER BY d) As sequence
FROM your_table
)
SELECT CASE WHEN sequence = 1 THEN a ELSE '' END As a
, CASE WHEN sequence = 1 THEN b ELSE '' END As b
, CASE WHEN sequence = 1 THEN c ELSE '' END As c
, d
FROM cte
ORDER
BY a
, b
, c
, d
The idea is to assign an incremental counter to each row, that restarts after each change of a + b + c.
We then use a conditional statement to show a value or not (basically only show on the first instance of each group)
The analytic ROW_NUMBER() function is good for this. I've made up column names because you didn't supply any. To assign a row number by customer, use something like this:
SELECT
Name,
Phone,
Address,
Response,
ROW_NUMBER() OVER (PARTITION BY Name, Phone, Address ORDER BY Response) AS CustRow
FROM myTable
That will assign row number within each customer. Try it yourself and I think it will make sense.
You can put it into a subquery or CTE from there and only show customer ID information like name, phone, and address when you're on the first row for each customer:
SELECT
CASE WHEN CustRow = 1 THEN Name ELSE '' END AS Name,
CASE WHEN CustRow = 1 THEN Phone ELSE '' END AS Phone,
CASE WHEN CustRow = 1 THEN Address ELSE '' END AS Address,
Response
FROM (
SELECT
Name,
Phone,
Address,
Response,
ROW_NUMBER() OVER (PARTITION BY Name, Phone, Address ORDER BY Response) AS CustRow
FROM myTable) custSubquery
ORDER BY Name, Phone, Address
The custSubquery on the second-to-last line is because SQL Server requires all subqueries to be aliased, even if the alias isn't used.
The most important thing is to determine how your last column will be ordered for display and to make sure that it's consistent in the ROW_NUMBER() function as well as the final ORDER BY.
If you need more help, please supply table and column names, and specify how results are ordered within each customer.

How can I Pivot a table in DB2? [duplicate]

This question already has answers here:
Pivoting in DB2
(3 answers)
Closed 5 years ago.
I have table A, below, where for each unique id, there are three codes with some value.
ID Code Value
---------------------
11 1 x
11 2 y
11 3 z
12 1 p
12 2 q
12 3 r
13 1 l
13 2 m
13 3 n
I have a second table B with format as below:
Id Code1_Val Code2_Val Code3_Val
Here there is just one row for each unique id. I want to populate this second table B from first table A for each id from the first table.
For the first table A above, the second table B should come out as:
Id Code1_Val Code2_Val Code3_Val
---------------------------------------------
11 x y z
12 p q r
13 l m n
How can I achieve this in a single SQL query?
select Id,
max(case when Code = '1' then Value end) as Code1_Val,
max(case when Code = '2' then Value end) as Code2_Val,
max(case when Code = '3' then Value end) as Code3_Val
from TABLEA
group by Id
SELECT Id,
max(DECODE(Code, 1, Value)) AS Code1_Val,
max(DECODE(Code, 2, Value)) AS Code2_Val,
max(DECODE(Code, 3, Value)) AS Code3_Val
FROM A
group by Id
If your version doesn't have DECODE(), you can also use this:
INSERT INTO B (id, code1_val, code2_val, code3_val)
WITH Ids (id) as (SELECT DISTINCT id
FROM A) -- Only to construct list of ids
SELECT Ids.id, a1.value, a2.value, a3.value
FROM Ids -- or substitute the actual id table
JOIN A a1
ON a1.id = ids.id
AND a1.code = 1
JOIN A a2
ON a2.id = ids.id
AND a2.code = 2
JOIN A a3
ON a3.id = ids.id
AND a3.code = 3
(Works on my V6R1 DB2 instance, and have an SQL Fiddle Example).
Here is a SQLFiddle example
insert into B (ID,Code1_Val,Code2_Val,Code3_Val)
select Id, max(V1),max(V2),max(V3) from
(
select ID,Value V1,'' V2,'' V3 from A where Code=1
union all
select ID,'' V1, Value V2,'' V3 from A where Code=2
union all
select ID,'' V1, '' V2,Value V3 from A where Code=3
) AG
group by ID
Here is the SQL Query:
insert into pivot_insert_table(id,code1_val,code2_val, code3_val)
select * from (select id,code,value from pivot_table)
pivot(max(value) for code in (1,2,3)) order by id ;
WITH Ids (id) as
(
SELECT DISTINCT id FROM A
)
SELECT Ids.id,
(select sub.value from A sub where Ids.id=sub.id and sub.code=1 fetch first rows only) Code1_Val,
(select sub.value from A sub where Ids.id=sub.id and sub.code=2 fetch first rows only) Code2_Val,
(select sub.value from A sub where Ids.id=sub.id and sub.code=3 fetch first rows only) Code3_Val
FROM Ids
You want to pivot your data. Since DB2 has no pivot function, yo can use Decode (basically a case statement.)
The syntax should be:
SELECT Id,
DECODE(Code, 1, Value) AS Code1_Val,
DECODE(Code, 2, Value) AS Code2_Val,
DECODE(Code, 3, Value) AS Code3_Val
FROM A