convert rows as column (like PIVOT) is not working if the number of values increases in SQL Server - sql

I want to convert rows as column (like PIVOT) and i am unable to get if the number of values increases.
Below is my table.
i want the output like this.
I have used the following queries to acheive this but no luck.
Query1
Create table #temp(instanceid int, submissionid int, name1 varchar(20), value1 varchar(20))
insert into #temp(instanceid,submissionid,name1,value1)
Select 5151,5532,'Question_1','Y'
union
Select 5151,5532,'First','Mujda'
union
Select 5151,5532,'Last','Zhublawar'
union
Select 5151,5532,'Question_1','Y'
union
Select 5151,5532,'First','Mujda1'
union
Select 5151,5532,'Last','Zhublawar1'
union
Select 5151,5532,'Question_1','Y'
union
Select 5152,5533,'First','Muthu'
union
Select 5151,5533,'Last','Kumar'
union
Select 5152,5533,'Question_1','Y'
union
Select 5152,5533,'First','Muthu1'
union
Select 5152,5533,'Last','Kumar1'
GO
DECLARE #SQLQuery AS NVARCHAR(MAX)
DECLARE #PivotColumns AS NVARCHAR(MAX)
DECLARE #PivotValues AS NVARCHAR(MAX)
--Get unique values of pivot column
SELECT #PivotColumns= COALESCE(#PivotColumns + ',','') + QUOTENAME(seq)
FROM (
select (cast(row_number() over(partition by name1 order by name1) as varchar(10)) + name1) as seq
from #temp group by value1,name1,instanceid
) AS PivotExample
--Create the dynamic query with all the values for
--pivot column at runtime
SET #SQLQuery =
N'SELECT instanceid,submissionid, ' + #PivotColumns + '
FROM #temp
PIVOT( MAX(value1)
FOR name1 IN (' + #PivotColumns + ')) AS P'
--Execute dynamic query
EXEC sp_executesql #SQLQuery
DROP TABLE #temp
Query2:
Create table #temp(instanceid int, submissionid int, name1 varchar(20), value1 varchar(20))
insert into #temp(instanceid,submissionid,name1,value1)
Select 5151,5532,'Question_1','Y'
union
Select 5151,5532,'First','Mujda'
union
Select 5151,5532,'Last','Zhublawar'
union
Select 5151,5532,'Interest','100'
select * from(
Select
instanceid,
submissionid,
[1st Ownership First Name] = Case when name1='First' then value1 end,
[1st Ownership Last Name] = Case when name1='Last' then value1 end,
[1st Ownership Question] = Case when name1='Question_1' then value1 end
from #temp
group by instanceid,submissionid,name1,value1
) P where p.[1st Ownership First Name] is not null or p.[1st Ownership Last Name] is not null or p.[1st Ownership Question] is not null
drop table #temp

You need an extra field to get some order in that data with all the duplicates.
For example a primary key.
Then you can use a pivot with a row_number that uses that extra field in the order by.
Then concat the row_number with the name1, and Pivot on those.
For example :
create table #temp (id int identity(1,1), instanceid int, submissionid int, name1 varchar(20), value1 varchar(20));
insert into #temp(instanceid,submissionid,name1,value1) values
(5151,5532,'First','Mujda'),
(5151,5532,'Last','Zhublawar'),
(5151,5532,'Question_1','Y'),
(5151,5532,'First','Mujda1'),
(5151,5532,'Last','Zhublawar1'),
(5151,5532,'Question_1','Y'),
(5151,5532,'First','Mujda1'),
(5151,5532,'Last','Zhublawar1'),
(5151,5532,'Question_1','Y'),
(5151,5533,'First','Muthu'),
(5151,5533,'Last','Kumar'),
(5151,5533,'Question_1','Y'),
(5151,5534,'First','Suresh'),
(5151,5534,'Last','Kumar'),
(5151,5534,'Question_1','Y'),
(5151,5534,'First','Suresh1'),
(5151,5534,'Last','Kumar1'),
(5151,5534,'Question_1','Y');
SELECT
instanceid,
submissionid,
[First1] as [1st First], [Last1] as [1st Last], [Question_11] as [1st Question_1],
[First2] as [2nd First], [Last2] as [2nd Last], [Question_12] as [2nd Question_1],
[First3] as [3rd First], [Last3] as [3rd Last], [Question_13] as [3rd Question_1]
FROM (
select
instanceid,
submissionid,
concat(name1, row_number() over (partition by instanceid, submissionid, name1 order by id)) as name_rn, value1
from #temp
where name1 in ('First','Last','Question_1')
) t
PIVOT( MAX(value1)
FOR name_rn IN (
[First1], [Last1], [Question_11],
[First2], [Last2], [Question_12],
[First3], [Last3], [Question_13]
)
) AS Pvt;
To do it the dynamic way, here's some SQL to generate a #SQL variable.
declare #T table (name1 varchar(20));
insert into #T select name1 from #temp group by name1 order by name1;
declare #SQL nvarchar(max);
declare #Fields1 nvarchar(max);
declare #Fields2 nvarchar(max);
SELECT #Fields2 = STUFF((select ', ' + quotename(name1) from #T order by name1 FOR XML PATH('')),1,1,'');
SET #Fields2 = replace(#Fields2,']','1]')+','+char(13)+replace(#Fields2,']','2]')+','+char(13)+replace(#Fields2,']','3]');
SELECT #Fields1 = STUFF((select ',$' + quotename(name1+n)+' as '+quotename(nx+' '+name1) from (
select '1' as n, '1st' as nx, name1 from #T union all
select '2', '2nd' as nx, name1 from #T union all
select '3', '3rd' as nx, name1 from #T
) q order by n, name1 FOR XML PATH('')),1,1,'');
SET #Fields1 = replace(#Fields1,'$',char(13));
SET #SQL =
'SELECT
instanceid,
submissionid,'+#Fields1+'
FROM (
select
instanceid,
submissionid,
concat(name1, row_number() over (partition by instanceid, submissionid, name1 order by id)) as name_rn, value1
from #temp
) t
PIVOT( MAX(value1)
FOR name_rn IN ('+char(13)+#Fields2+')
) AS Pvt';
select #SQL;

--Get unique values of pivot column
SELECT #PivotColumns= COALESCE(#PivotColumns + ',','') + QUOTENAME(seq)
FROM (
select DISTINCT (name1) as seq
from #temp group by value1,name1,instanceid
) AS PivotExample
This are is your problem you are concatenating a row_number into the name1 value which would always be null. Your column list needs to be the DISTINCT values in name1 NOT what you want them to be in the end. If you want to rename columns you would alias them after the pivot or change the value in name1 prior to running the pivot.
So in the above code I removed the ROW_NUMBER() and added DISTINCT to the derived table.
Also note the test data you included has some instanceid & submissionid combinations that are missing certain fields like first name, or question. It looks like when copying and pasting you just didn't correct the correlations.

Related

Multiple Rows into One Row multiple columns

We need to list all numbers as a flat data set, how can we do that?
Table Name: Telephone
ID TYPE NUMBER
==================================
123 MN 042153939
123 HN 2242116
123 MN 1234567890
123 HN 12345678
Create Table Telephone
(
ID Integer,
Type char(3),
Number Varchar(20)
);
insert into Telephone values
(123, 'MN', '042153939'),
(123, 'HN', '2242116'),
(123, 'MN', '1234567890'),
(123, 'HN', '12345678');
I want SQL to return data in this format
ID MN#1 Mn#2 HN#1 HN#2
================================================
123 042153939 1234567890 2242116 12345678
Dynamic approach
Init
DROP TABLE IF EXISTS #Telephone;
CREATE TABLE #Telephone(ID INT,Type CHAR(3),Number VARCHAR(20));
INSERT INTO #Telephone (ID,Type,Number) VALUES
(123, 'MN', '042153939'),
(123, 'HN', '2242116'),
(123, 'MN', '1234567890'),
(123, 'HN', '12345678');
The code
DECLARE #ColumnList NVARCHAR(MAX);
SELECT #ColumnList = STUFF((SELECT ',[' + RTRIM(t.[Type]) + '#'
+ CONVERT(NVARCHAR(255),ROW_NUMBER()OVER(PARTITION BY t.[Type] ORDER BY t.ID)) + ']'
FROM #Telephone t FOR XML PATH(''),TYPE).value('(./text())[1]','NVARCHAR(MAX)'),1,1,'')
;
DECLARE #sql NVARCHAR(MAX) = '';
SET #sql = N'
SELECT ID,' + #ColumnList + N'
FROM (
SELECT t.ID,t.Number, RTRIM(t.[Type]) + ''#'' + CONVERT(NVARCHAR(255),ROW_NUMBER()OVER(PARTITION BY t.[Type] ORDER BY t.ID)) AS [Type]
FROM #Telephone t
) a
PIVOT(MAX(a.Number) FOR a.Type IN (' + #ColumnList + N')) p
'
;
--PRINT #sql
IF #sql IS NOT NULL EXEC(#sql);
try pivoting like below :
SELECT first_column AS <first_column_alias>,
[pivot_value1], [pivot_value2], ... [pivot_value_n]
FROM
(<source_table>) AS <source_table_alias>
PIVOT
(
aggregate_function(<aggregate_column>)
FOR <pivot_column> IN ([pivot_value1], [pivot_value2], ... [pivot_value_n])
) AS <pivot_table_alias>;
We can try using a pivot query with the help of ROW_NUMBER():
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (ORDER BY TYPE DESC, NUMBER) rn
FROM Telephone
)
SELECT
ID,
MAX(CASE WHEN rn = 1 THEN NUMBER END) AS [MN#1],
MAX(CASE WHEN rn = 2 THEN NUMBER END) AS [MN#2],
MAX(CASE WHEN rn = 3 THEN NUMBER END) AS [HN#3],
MAX(CASE WHEN rn = 4 THEN NUMBER END) AS [HN#4]
FROM cte
GROUP BY ID;
You may try this. with row_number() and pivot.
For more info about pivot you may find this link PIVOT.
; with cte as (
select row_number() over (partition by type order by id ) as Slno, * from Telephone
)
, ct as (
select id, type + '#' + cast(slno as varchar(5)) as Type, values from cte
)
select * from (
select * from ct
) as d
pivot
( max(values) for type in ( [MN#1],[Mn#2],[HN#1],[HN#2] )
) as p

Convert dynamic text data rows into multiple columns

I'm trying to convert my result table which consist of multiple rows into multiple columns. below are my sample result before and after:
Before:
After:
Got a some idea from here but still no luck.
Update 1:
select #cols = STUFF((select ',' + QUOTENAME(institution) + ',' + QUOTENAME(intstatus)
From #temp
group by refno,frmstatus
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
i have no idea how to insert each column into the pivot query
set #query = 'select refno, frmstatus,' + #cols + '
from (
select refno, frmstatus, institution, intstatus from #temp
) x
pivot
(
???????
)
Please try the below query:
CREATE TABLE #temp(refno nvarchar(20), firmstatus nvarchar(20), institution nvarchar(20), intstatus nvarchar(20), ranking int)
DECLARE #qu NVARCHAR(MAX), #pcol NVARCHAR(MAX)
INSERT INTO #temp
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY refno ORDER BY institution ASC) AS ranking
FROM temp
SELECT #pcol=
STUFF((
SELECT
DISTINCT N', Institution'+ CAST (ranking AS NVARCHAR(25)) +', '+ N'Intstatus'+ CAST (ranking AS NVARCHAR(25))
FROM #temp
FOR XML PATH('')),1,1,'')
SET #qu=N'SELECT refno, firmstatus,'+ #pcol +
N' FROM
(
select refno,firmstatus,ColData,colheader+ CAST(ranking as varchar) as colnames from
(select * from #temp)s
UNPIVOT
(ColData for colheader in ([institution], [intstatus])) up
)S
PIVOT
(MAX(ColData) FOR colnames IN ('+#pcol +N')) AS piv'
EXEC sp_executesql #qu -- execute the dynamic sql
DROP TABLE #temp -- remove the temp table
The temp table in above script was created like below
--create table temp( refno nvarchar(20), firmstatus nvarchar(20), institution nvarchar(20), intstatus nvarchar(20))
--insert into temp values
--('AAA/1','Active','InstA','Ongoing'),
--('AAA/1','Active','InstB','Ongoing'),
--('AAA/1','Active','InstC','Ongoing'),
--('AAA/2','Active','InstA','Ongoing'),
--('AAA/2','Active','InstB','Ongoing')
Result received:
If repeated columns number is small and restricted, you can use simple solution:
WITH A AS (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY refno, fmstatus ORDER BY institution) n
FROM myTable
),
B AS (
SELECT refno, fmstatus,
CASE WHEN n=1 THEN institution END institution,
CASE WHEN n=1 THEN intstatus END intstatus,
CASE WHEN n=2 THEN institution END institution1,
CASE WHEN n=2 THEN intstatus END intstatus1,
CASE WHEN n=3 THEN institution END institution2,
CASE WHEN n=3 THEN intstatus END intstatus2
FROM A
)
SELECT refno, fmstatus,
MAX(institution) institution,
MAX(intstatus) intstatus,
MAX(institution1) institution1,
MAX(intstatus1) intstatus1,
MAX(institution2) institution2,
MAX(intstatus2) intstatus2
FROM B
GROUP BY refno, fmstatus
Otherwise use PIVOT

converting rows to columns dynamicaly based on conditions

In the below image I have current data and expected data based on the first three columns the data need to get transpose
Table query:
drop table ##Test
CREATE TABLE ##Test
(paymentid varchar(30),
claimid bigint,
Lineno1 int,
groupcode varchar(2),
Carc int,
adjustmentamt float,
RARC varchar(30),
Edit1 varchar(30),
Remit varchar(30),
)
INSERT INTO ##Test
(paymentid ,
claimid ,
Lineno1 ,
groupcode ,
Carc ,
adjustmentamt ,
RARC ,
Edit1 ,
Remit )
VALUES
('QP18502291',14205893514,2,'CO',84,75.55,'N20','','D18')
('QP15930339',14127612308,1,'OA',23,263,'','ClaimDetail.COBAmt','') ,
('QP15930339',14127612308,1,'OA',23,21.69,'','ClaimDetail.COBAmt',''),
('QP18502291',14205893514,2,'OA',23,78.77,'','ClaimDetail.COBAmt',''),
('QP18502291',14205893514,2,'OA',97,66.55,'N20','','D18')
select * from ##Test
Possible duplicate post and take a look at the solution.
See SQL Fiddle with Demo.
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(col+cast(rn as varchar(10)))
from
(
select row_number() over(partition by paymentid order by Lineno1) rn
from ##Test
) d
cross apply
(
select 'groupcode', 1 union all
select 'carc', 2 union all
select 'adjustmentamt', 3 UNION ALL
SELECT 'rarc',4 UNION ALL
SELECT 'Edit1' ,5 UNION ALL
SELECT 'remit',6
) c (col, so)
group by col, rn, so
order by rn, so
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT paymentid, claimid,Lineno1,' + #cols + '
from
(
select paymentid, claimid,Lineno1, col+cast(rn as varchar(10)) col, value
from
(
select paymentid, claimid,Lineno1, groupcode, carc, adjustmentamt,rarc,Edit1,remit,
row_number() over(partition by paymentid order by Lineno1) rn
from ##Test
) t
cross apply
(
select ''groupcode'', groupcode union all
select ''carc'', cast(carc as varchar(50)) union all
select ''adjustmentamt'', cast(adjustmentamt as varchar(50)) union all
select ''rarc'', rarc union all
select ''Edit1'' , Edit1 union all
select ''remit'' , remit
) c (col, value)
) x pivot
(
max(value)
for col in (' + #cols + ')
) p '
execute(#query)

Pivot multiple columns in SQL Server 2008

I am trying to convert column to rows. I am beginner to PIVOT. Tried below code but error is coming as 'Error converting data type nvarchar to datetime.
The incorrect value "Tot_GPS_Cnt" is supplied in the PIVOT operator.'
CREATE TABLE #tbl_Res1
(
CallDate DATETIME,
Tot_GPS_Cnt INT,
Tot_GND_Cnt INT,
Per_Ratio NUMERIC(10,2)
)
INSERT INTO #tbl_Res1
SELECT '2015-04-24 00:00:00.000','40','26','65.00' UNION ALL
SELECT '2015-04-25 00:00:00.000','22','14','63.64' UNION ALL
SELECT'2015-04-26 00:00:00.000','27','21','77.78' UNION ALL
SELECT'2015-04-27 00:00:00.000','41','23','56.10'
Source Table
Desired Output
I have tried with below query bu failing. Please help. Thanks in advance
SELECT CallDate=col, Tot_GPS_Cnt, Tot_GND_Cnt, Per_Ratio
FROM
( select CallDate, col, value from #tbl_Res1
cross apply
(
SELECT 'Tot_GPS_Cnt',cast(Tot_GPS_Cnt as varchar(10)) UNION ALL
SELECT 'Tot_GND_Cnt', cast(Tot_GND_Cnt as varchar(10)) UNION ALL
SELECT 'Per_Ratio', cast(Per_Ratio as varchar(10))
) c (col,value)
) d
PIVOT
(
max(value) for CallDate in ([Tot_GPS_Cnt], [Tot_GND_Cnt], [Per_Ratio])
) as piv
The correct syntax for PIVOT is:
PIVOT
(
<aggregation function>(<column being aggregated>)
FOR
[<column that contains the values that will become column headers>]
IN ( [first pivoted column], [second pivoted column],
... [last pivoted column])
) AS <alias for the pivot table>
Hence, you have to place date values not [Tot_GPS_Cnt], [Tot_GND_Cnt], [Per_Ratio] in place of pivoted columns:
SELECT CallDate=col, [2015-04-24], [2015-04-25], [2015-04-26], [2015-04-27]
FROM
( select CallDate, col, value from #tbl_Res1
cross apply
(
SELECT 'Tot_GPS_Cnt',cast(Tot_GPS_Cnt as varchar(10)) UNION ALL
SELECT 'Tot_GND_Cnt', cast(Tot_GND_Cnt as varchar(10)) UNION ALL
SELECT 'Per_Ratio', cast(Per_Ratio as varchar(10))
) c (col,value)
) d
PIVOT
(
max(value) for CallDate in ([2015-04-24], [2015-04-25], [2015-04-26], [2015-04-27])
) as piv
Thank you #Giorgos Betsos. I just made dynamic solution. Just wanted to share with all.
DECLARE #coldata VARCHAR(500);
DECLARE #sql VARCHAR(MAX);
SELECT #coldata = COALESCE(#coldata + '], [', '') + CONVERT(VARCHAR(10),calldate,110)
FROM #tbl_Res1;
SELECT #coldata = '[' + #coldata +']';
SELECT #coldata;
SET #sql = 'SELECT CallDate=col, ' + #coldata + '
FROM
( SELECT CallDate, col, value FROM #tbl_Res1
cross apply
(
SELECT ''Tot_GPS_Cnt'', CAST(Tot_GPS_Cnt as VARCHAR(10)) UNION ALL
SELECT ''Tot_GND_Cnt'', CAST(Tot_GND_Cnt as VARCHAR(10)) UNION ALL
SELECT ''Per_Ratio'', CAST(Per_Ratio as VARCHAR(10))
) c (col,value)
) d
PIVOT
(
MAX(value) FOR CallDate IN (' + #coldata + ')
) as piv'
SELECT #sql;
EXECUTE (#SQL);

SQL query problem

Let´s say I have two tables, "Garden" and "Flowers". There is a 1:n-relationship between these tables, because in a garden can be many flowers. Is it possible to write an SQL query which returns a result with the following structure:
GardenName Flower1Name Flower2Name .... (as long as there are entries in flowers)
myGarden rose tulip
CREATE TABLE #Garden (Id INT, Name VARCHAR(20))
INSERT INTO #Garden
SELECT 1, 'myGarden' UNION ALL
SELECT 2, 'yourGarden'
CREATE TABLE #Flowers (GardenId INT, Flower VARCHAR(20))
INSERT INTO #Flowers
SELECT 1, 'rose' UNION ALL
SELECT 1, 'tulip' UNION ALL
SELECT 2, 'thistle'
DECLARE #ColList nvarchar(max)
SELECT #ColList = ISNULL(#ColList + ',','') + QUOTENAME('Flower' + CAST(ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS VARCHAR))
FROM #Flowers WHERE GardenId = (
SELECT TOP 1 GardenId
FROM #Flowers
ORDER BY COUNT(*) OVER (PARTITION BY GardenId) DESC
)
EXEC (N'
;WITH cte As
(
SELECT *, ''Flower'' + CAST(ROW_NUMBER() OVER (PARTITION BY GardenId ORDER BY (SELECT 0)) AS VARCHAR) RN
FROM #Flowers F
)
SELECT Name,' + #ColList + N'
FROM cte
JOIN #Garden g ON g.Id = GardenId
PIVOT (MAX(Flower) FOR RN IN (' + #ColList + N')) Pvt')
DROP TABLE #Garden
DROP TABLE #Flowers
Returns
Name Flower1 Flower2
-------------------- -------------------- --------------------
myGarden rose tulip
yourGarden thistle NULL
Look at using Pivot in SQL Server. Here is a good link that goes over how it works:
http://www.kodyaz.com/articles/t-sql-pivot-tables-in-sql-server-tutorial-with-examples.aspx
Ok, i think i got it working. Try this:
with temp as
(
select 'myGarden' as name, 'test1' as flower
union
select 'myGarden','test2'
union
select 'myGarden','test5'
union
select 'abeGarden','test4'
union
select 'abeGarden','test5'
union
select 'martinGarden', 'test2'
)
select* from temp
pivot
(
max(flower)
for flower in (test1,test2,test3,test4,test5)
) PivotTable
You could also make the values in the in clause dynamic. Since this is a CTE i can't in my example.
Dynamic SQL with a cursor is the only way I can think of, and it won't be pretty.
If you only want the results for one garden at a time this would give you the data:
select gardenName from tblGarden where gardenid = 1
Union ALL
select tblFLowers.flowerName from tblFlowers where gardenid = 1