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
Related
I have a table that has many InsuranceNo's for unique MemberIDs. If there are more than one InsuranceNo, I want the InsuranceNo's to shift to a column, so in the end there is one line per MemberID, with all the iterations of that ID's InsuranceNo's as a Column.
MemberID InsuranceNo
--------------------------
123456 dser
124571 jklh
123456 abcd
I want it to look like this:
MemberID InsuranceNo1 InsuranceNo2
-----------------------------------------------------
123456 dser abcd
124571 jklh
Thank you!
Yet another option... Just change "YourTable" to your actual table name.
Example
Declare #SQL varchar(max) = '
Select *
From (
Select MemberID
,Item = concat(''InsuranceNo'',row_number() over (Partition By MemberID Order By (Select NULL)))
,Value = InsuranceNo
From YourTable
) A
Pivot (max([Value]) For [Item] in (' + Stuff((Select ','+QuoteName(concat('InsuranceNo',ColNr))
From (Select Distinct ColNr=row_number() over (Partition By MemberID Order By (Select NULL)) from YourTable ) A
For XML Path('')),1,1,'') + ') ) p'
--Print #SQL
Exec(#SQL);
Returns
MemberID InsuranceNo1 InsuranceNo2
123456 dser abcd
124571 jklh NULL
If it helps wrap your head around PIVOT, the SQL Generated looks like this:
Select *
From (
Select MemberID
,Item = concat('InsuranceNo',row_number() over (Partition By MemberID Order By (Select NULL)))
,Value = InsuranceNo
From YourTable
) A
Pivot (max([Value]) For [Item] in ([InsuranceNo1],[InsuranceNo2]) ) p
I prefer a dynamic cross tab to the dynamic pivot. I find the syntax far less obtuse and it is super easy if you need to add additional columns. Here is I would go about tackling this. Of course in your case you don't need a temp table because you have an actual table to use.
if OBJECT_ID('tempdb..#Something') is not null
drop table #Something
create table #Something
(
MemberID int
, InsuranceNo varchar(10)
)
insert #Something values
(123456, 'dser')
, (124571, 'jklh')
, (123456, 'abcd')
declare #StaticPortion nvarchar(2000) =
'with OrderedResults as
(
select *, ROW_NUMBER() over(partition by MemberID order by InsuranceNo) as RowNum
from #Something
)
select MemberID';
declare #DynamicPortion nvarchar(max) = '';
declare #FinalStaticPortion nvarchar(2000) = ' from OrderedResults Group by MemberID order by MemberID';
with E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E2
)
select #DynamicPortion = #DynamicPortion +
', MAX(Case when RowNum = ' + CAST(N as varchar(6)) + ' then InsuranceNo end) as InsuranceNo' + CAST(N as varchar(6)) + CHAR(10)
from cteTally t
where t.N <=
(
select top 1 Count(*)
from #Something
group by MemberID
order by COUNT(*) desc
)
select #StaticPortion + #DynamicPortion + #FinalStaticPortion
declare #SqlToExecute nvarchar(max) = #StaticPortion + #DynamicPortion + #FinalStaticPortion;
exec sp_executesql #SqlToExecute
I have a table and values as shown here
create table #example(id int primary key identity, cols varchar(255))
insert into #example(cols) values('HI,HELLO,BYE,TC')
insert into #example(cols) values('WHAT,ARE,YOU,DOING,HERE')
I need the resultant output as shown in the picture
Note : There is no limit for values i.e dynamic
This is what you are expecting
Schema:
CREATE TABLE #EXAMPLE(ID INT PRIMARY KEY IDENTITY, COLS VARCHAR(255))
INSERT INTO #EXAMPLE(COLS) VALUES('HI,HELLO,BYE,TC')
INSERT INTO #EXAMPLE(COLS) VALUES('WHAT,ARE,YOU,DOING,HERE')
Do split those comma separated values into Rows and Apply pivot
SELECT * FROM (
SELECT id
, ROW_NUMBER() OVER(PARTITION BY ID ORDER BY (SELECT 1)) AS ID2
, SPLT.CLMS.value('.','VARCHAR(MAX)') AS LIST FROM (
select id
, CAST( '<M>'+REPLACE(cols,',','</M><M>')+'</M>' AS XML) AS XML_COL from #example E
)E
CROSS APPLY E.XML_COL.nodes('/M') AS SPLT(CLMS)
)A
PIVOT
(
MAX(LIST) FOR ID2 IN ([1],[2],[3],[4],[5])
)PV
You will get result as
+----+------+-------+-----+-------+------+
| id | 1 | 2 | 3 | 4 | 5 |
+----+------+-------+-----+-------+------+
| 1 | HI | HELLO | BYE | TC | NULL |
| 2 | WHAT | ARE | YOU | DOING | HERE |
+----+------+-------+-----+-------+------+
Edit:
And now you need to go for dynamic pivot,since There is no limit for values.
DECLARE #COLS VARCHAR(MAX)='', #QRY VARCHAR(MAX)='';
SELECT #COLS =#COLS+'['+CAST( ID2 AS VARCHAR(10))+'],' FROM (
SELECT DISTINCT ROW_NUMBER() OVER(PARTITION BY ID ORDER BY (SELECT 1)) AS ID2 FROM (
select id, CAST( '<M>'+REPLACE(cols,',','</M><M>')+'</M>' AS XML) AS XML_COL from #example E
)E
CROSS APPLY E.XML_COL.nodes('/M') AS SPLT(CLMS)
)A
SELECT #COLS = LEFT(#COLS,LEN(#COLS)-1)
SELECT #QRY =
'
SELECT * FROM (
SELECT id
, ROW_NUMBER() OVER(PARTITION BY ID ORDER BY (SELECT 1)) AS ID2
, SPLT.CLMS.value(''.'',''VARCHAR(MAX)'') AS LIST FROM (
select id, CAST( ''<M>''+REPLACE(cols,'','',''</M><M>'')+''</M>'' AS XML) AS XML_COL from #example E
)E
CROSS APPLY E.XML_COL.nodes(''/M'') AS SPLT(CLMS)
)A
PIVOT
(
MAX(LIST) FOR ID2 IN ('+#COLS+ ')
)PV'
EXEC( #QRY)
Dynamic version base on split string then pivot table
Schema:
create table #example(id int primary key identity, cols varchar(255))
insert into #example(cols) values('HI,HELLO,BYE,TC')
insert into #example(cols) values('WHAT,ARE,YOU,DOING,HERE')
Calculate columns pivot
-- Calculate dynamic columns
;WITH temps AS
(
SELECT sd.* ,CAST('<x>' + replace(sd.cols, ',', '</x><x>') + '</x>' as xml) AS xmlText
FROM #example sd
),
temps1 AS
(
SELECT t.Id, t.cols, v.x.value('.','varchar(50)') AS Value
FROM temps t
CROSS APPLY
t.xmlText.nodes('/x') AS v(x)
)
SELECT #ColumnPivot = STUFF((SELECT DISTINCT CONCAT(',COL',row_number() OVER(PARTITION BY t.id ORDER BY t.Value)) FROM temps1 t FOR XML PATH('')), 1,1,'')
PRINT #ColumnPivot
Then PIVOT table
DECLARE #query nvarchar(max) =
CONCAT(N';WITH temps AS
(
SELECT sd.* ,CAST(''<x>'' + replace(sd.cols, '','', ''</x><x>'') + ''</x>'' as xml) AS xmlText
FROM #example sd
),
temps1 AS
(
SELECT t.Id, t.cols, v.x.value(''.'' , ''varchar(50)'') AS Value
FROM temps t
CROSS APPLY
t.xmlText.nodes(''/x'') AS v(x)
),
temps2 AS
(
SELECT t.* , CONCAT(''COL'',row_number() OVER(PARTITION BY t.id ORDER BY (select 1))) AS ColGroup
FROM temps1 t
)',
N'SELECT Id, cols, ' , #ColumnPiVot,
' FROM
(
select * from temps2
) AS src
PIVOT
(MAX(Value) FOR ColGroup IN (',#ColumnPiVot,')) AS pvt')
PRINT #query
exec sp_executesql #query
DROP TABLE #example
Demo link: Rextester
DECLARE #COLS AS NVARCHAR(MAX),
#QUERY AS NVARCHAR(MAX);
SET #COLS = STUFF((SELECT DISTINCT ',' + QUOTENAME(C.RN)
FROM (SELECT ID,TOKEN,COLS
,ROW_NUMBER() OVER (
PARTITION BY ID ORDER BY (
SELECT NULL
)
) AS RN
FROM #EXAMPLE
CROSS APPLY (
SELECT *
FROM UDF_SPLITSTRING(COLS, ',')
) A) C
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #QUERY = 'SELECT ID,cols, ' + #COLS + ' FROM
(
SELECT ID,TOKEN,cols
,ROW_NUMBER() OVER (
PARTITION BY ID ORDER BY (
SELECT NULL
)
) AS RN
FROM #EXAMPLE
CROSS APPLY (
SELECT *
FROM UDF_SPLITSTRING(COLS, '','')
) A
) X
PIVOT
(
MAX(TOKEN)
FOR rn IN (' + #COLS + ')
) P '
exec(#QUERY)
output
ID cols 1 2 3 4 5
1 HI,HELLO,BYE,TC HI HELLO BYE TC NULL
2 WHAT,ARE,YOU,DOING,HERE WHAT ARE YOU DOING HERE
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.
I have the following situation (heavily abstracted, please ignore bad design):
CREATE TABLE dbo.PersonTest (Id INT, name VARCHAR(255))
INSERT INTO dbo.PersonTest
(Id, name )
VALUES (1, 'Pete')
, (1, 'Marie')
, (2, 'Sam')
, (2, 'Daisy')
I am looking for the following result:
Id Name1 Name2
1 Marie Pete
2 Daisy Sam
So, for each Id, the rows should be merged.
Getting this result I used the following query:
WITH PersonRN AS
(
SELECT *
, ROW_NUMBER() OVER(PARTITION BY Id ORDER BY name) RN
FROM dbo.PersonTest
)
SELECT PT1.Id
, PT1.name Name1
, PT2.name Name2
FROM PersonRN AS PT1
LEFT JOIN PersonRN AS PT2 -- Left join in case there's only 1 name
ON PT2.Id = PT1.Id
AND PT2.RN = 2
WHERE PT1.RN = 1
Which works perfectly fine.
My question is: Is this the best way (best in terms of performance and resilience)? If, for example, one of these Id's has a third name, this third name is ignored by my query. I'm thinking the best way to deal with that would be dynamic SQL, which would be fine, but if it can be done without dynamic, I would prefer that.
Aside from dynamic PIVOT, you can do this using Dynamic Crosstab, which I prefer for readability.
SQL Fiddle
DECLARE #sql1 VARCHAR(1000) = '',
#sql2 VARCHAR(1000) = '',
#sql3 VARCHAR(1000) = ''
DECLARE #max INT
SELECT TOP 1 #max = COUNT(*) FROM PersonTest GROUP BY ID ORDER BY COUNT(*) DESC
SELECT #sql1 =
'SELECT
ID' + CHAR(10)
SELECT #sql2 = #sql2 +
' , MAX(CASE WHEN RN =' + CONVERT(VARCHAR(5), RN)
+ ' THEN name END) AS ' + QUOTENAME('Name' + CONVERT(VARCHAR(5), RN)) + CHAR(10)
FROM(
SELECT TOP(#max)
ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS RN
FROM sys.columns
)t
ORDER BY RN
SELECT #sql3 =
'FROM(
SELECT *,
RN = ROW_NUMBER() OVER(PARTITION BY ID ORDER BY name)
FROM PersonTest
)t
GROUP BY ID
ORDER BY ID'
PRINT (#sql1 + #sql2 + #sql3)
EXEC (#sql1 + #sql2 + #sql3)
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)