How to create dynamic pivot query in SQL - sql

I am trying to create dynamic pivot query in SQL but my issue is that the contract id and the tier desc columns are both dynamic and I could not figure out how to solve this issue. I have something like this:
and this is the output I would like to see

This can be done with repeating column names, however, I can't imagine why one would want this.
The #Col is where we apply the Alias ...[#] as [Tier Value]...
Example
Declare #Col varchar(max) = Stuff((Select Distinct ',' + concat(QuoteName(row_number() over (Partition By ContractID Order by TierDesc)),' as [Tier Value]') From Yourtable Order by 1 For XML Path('')),1,1,'')
Declare #SQL varchar(max) = Stuff((Select Distinct ',' + QuoteName(row_number() over (Partition By ContractID Order by TierDesc)) From Yourtable Order by 1 For XML Path('')),1,1,'')
Select #SQL = '
Select ContractID,'+#Col+'
From (
Select ContractID
,TierDesc
,ColNr = row_number() over (Partition By ContractID Order by TierDesc)
From YourTable
) Src
Pivot (max(TierDesc) for ColNr in ('+#SQL+') ) pvt
'
Exec(#SQL)
Returns
ContractID Tier Value Tier Value Tier Value
123 tier1 tier2 NULL
555 tier4 tier5 tier6
652 tier0 tier4 NULL
EDIT - Then generated SQL Looks like this
Select ContractID
,[1] as [Tier Value]
,[2] as [Tier Value]
,[3] as [Tier Value]
From (
Select ContractID
,TierDesc
,ColNr = row_number() over (Partition By ContractID Order by TierDesc)
From YourTable
) Src
Pivot (max(TierDesc) for ColNr in ([1],[2],[3]) ) pvt
EDIT 2
Select Distinct
ColNr = row_number() over (partition by ContractID Order By TierDesc)
From Yourtable

Related

convert multiple rows to columns

Source:
ItemId
ItemName
Nutrient
GAV
A
Beef
Vit A
1
A
Beef
Vit B
2
A
Beef
Vit C
3
target:
Id
Name
Nut1
GAV1
Nut2
GAV2
Nut3
GAV3
A
Beef
VitA
1
VitB
2
VitC
3
How can we achieve this with ms-sql query?
Assuming GAV is NOT sequential as presented, we'll have to use the window function row_number() and some Dynamic SQL
Example or dbFiddle
Declare #SQL varchar(max)
Select #SQL = string_agg( concat('[',ColName,ColNr,']','=','max(case when ColNr =',ColNr,' then ',ColName,' end)') , ',') within group (ORDER BY ColNr,ColName Desc)
From (values ('Nutrient'),('GAV') ) A(ColName)
Cross Join ( Select Distinct ColNr = row_number() over( partition by ItemID order by GAV) from YourTable ) B
Set #SQL = '
Select ItemID
,ItemName
,' + #SQL + '
From ( Select *
,ColNr = row_number() over( partition by ItemID order by GAV )
From YourTable
) A
Group By ItemID
,ItemName
'
Exec(#SQL)
Results
UPDATE 2016 Version
Declare #SQL varchar(max) = ''
Select #SQL = #SQL + concat(',','[',ColName,ColNr,']','=','max(case when ColNr =',ColNr,' then ',ColName,' end)')
From (values ('Nutrient'),('GAV') ) A(ColName)
Cross Join ( Select Distinct ColNr = row_number() over( partition by ItemID order by GAV) from YourTable ) B
Order By ColNr,ColName Desc
Set #SQL = '
Select ItemID
,ItemName
' + #SQL + '
From ( Select *
,ColNr = row_number() over( partition by ItemID order by GAV )
From YourTable
) A
Group By ItemID
,ItemName
'
Exec(#SQL)

Convert three rows values into columns, NOT as comma separated value

I have table structure like
select catalog_item_id,metal_type,metal_color
from catalog_item_castings
where catalog_Item_Id =465173
It returns output as:
And I want output as:
And I want to insert this data into new temp table in SQL Server.
Thanks in advance.
Conditional aggregation is an option:
SELECT
catalog_item_id,
MAX(CASE WHEN rn % 3 = 1 THEN CONCAT(metal_type, '/', metal_color) END) AS Casting_1,
MAX(CASE WHEN rn % 3 = 2 THEN CONCAT(metal_type, '/', metal_color) END) AS Casting_2,
MAX(CASE WHEN rn % 3 = 0 THEN CONCAT(metal_type, '/', metal_color) END) AS Casting_3
FROM (
SELECT
catalog_item_id, metal_type, metal_color, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS rn
FROM (VALUES
(465173, 'na', 'METALCOLOR'),
(465173, 'na', 'METAL-001'),
(465173, 'na', 'na')
) catalog_item_castings (catalog_item_id, metal_type, metal_color)
WHERE catalog_Item_Id = 465173
) t
GROUP BY catalog_item_id
-- or if you have more than three rows per [catalog_item_id]
-- GROUP BY catalog_item_id, (rn - 1) / 3
Result:
catalog_item_id Casting_1 Casting_2 Casting_3
-------------------------------------------------
465173 na/METALCOLOR na/METAL-001 na/na
You can use Conditional Aggregation within a Dynamic Pivot Statement in order to include all distinct combinations of the columns [metal_type] and [metal_color], even different values for combinations are inserted in the future :
DECLARE #cols AS NVARCHAR(MAX), #query AS NVARCHAR(MAX)
SELECT #cols = (SELECT STRING_AGG(CONCAT('MAX(CASE WHEN [dr]=',dr,
' THEN CONCAT([metal_type],''/'',[metal_color]) END) AS [Casting_',dr,']'),',')
WITHIN GROUP ( ORDER BY dr )
FROM
(
SELECT DISTINCT
DENSE_RANK() OVER
(PARTITION BY [catalog_item_id]
ORDER BY CONCAT([metal_type],[metal_color])) AS dr
FROM [catalog_item_castings] ) c);
SET #query =
'SELECT [catalog_item_id],'+ #cols +
' FROM
(
SELECT *, DENSE_RANK() OVER
( PARTITION BY [catalog_item_id]
ORDER BY CONCAT([metal_type], [metal_color]) ) AS dr
FROM [catalog_item_castings]
) c
GROUP BY [catalog_item_id]';
EXEC sp_executesql #query;
Demo

SQL Pivot multiple columns without forcing aggregate

I need to pivot out some denormalized data but it repeats so I need it to pivot out the columns and then return multiple rows.
I have a table like this
INSERT #TheTable
VALUES
('StockCode' ,'a'),
('Warehouse' ,'b'),
('TrnYear' ,'c'),
('TrnMonth' ,'d'),
('EntryDate' ,'e'),
('TrnTime' ,'f'),
('StockCode' ,'1'),
('Warehouse' ,'2'),
('TrnYear' ,'3'),
('TrnMonth' ,'4'),
('EntryDate' ,'5'),
('TrnTime' ,'6')
But when I pivot it only returns one row:
SELECT StockCode,
Warehouse,
TrnYear,
TrnMonth,
TrnTime,
EntryDate
FROM #TheTable AS src
PIVOT (MAX(column_value)
FOR COLUMN_NAME in ([TrnYear], [TrnMonth], [EntryDate], [TrnTime], [StockCode], [Warehouse])) AS piv
Result:
StockCode Warehouse TrnYear TrnMonth TrnTime EntryDate
-------------------------------------------------------------
a b c d f e
But I need it to return
StockCode Warehouse TrnYear TrnMonth TrnTime EntryDate
-------------------------------------------------------------
a b c d f e
1 2 3 4 5 6
You can use window functions first, then conditional aggregation:
select
max(case when column_name = 'StockCode' then column_value end) StockCode,
max(case when column_name = 'Warehouse' then column_value end) Warehouse,
max(case when column_name = 'TrnYear' then column_value end) TrnYear,
max(case when column_name = 'TrnMonth' then column_value end) TrnMonth,
max(case when column_name = 'TrnTime' then column_value end) TrnTime,
max(case when column_name = 'EntryDate' then column_value end) EntryDate
from (
select t.*,
row_number() over(partition by column_name order by column_value) rn
from #TheTable t
) t
group by rn
You can use ROW_NUMBER() Analytic function :
SELECT *
FROM
(
SELECT column_name, column_value,
ROW_NUMBER() OVER (PARTITION BY column_name ORDER BY column_value) AS rn
FROM #TheTable
) q
PIVOT
( MAX(column_value)
FOR column_name in ([TrnYear], [TrnMonth], [EntryDate], [TrnTime], [StockCode], [Warehouse])
) AS piv
or more dynamically, use :
DECLARE #cols AS NVARCHAR(MAX), #query AS NVARCHAR(MAX)
SET #cols = ( SELECT STRING_AGG(column_name,',')
FROM (SELECT DISTINCT column_name
FROM [#TheTable] ) q );
SET #query =
N'SELECT *
FROM
(
SELECT column_name, column_value,
ROW_NUMBER() OVER
(PARTITION BY [column_name] ORDER BY [column_value]) AS rn
FROM [#TheTable]
) f
PIVOT
(
MAX([column_value]) FOR [column_name] IN (' + #cols + N')
) AS piv '
EXEC sp_executesql #query;
Demo
The problem was that I needed a row_number per every group of column names. So the below worked
SELECT DISTINCT TrnYear,
TrnMonth,
EntryDate,
TrnTime,
StockCode,
Warehouse
FROM
(SELECT (ROW_NUMBER() OVER (ORDER BY dw_view_change_event_nr) - 1) / 6 + 1 AS rn,
COLUMN_NAME ,
column_value
FROM
#TheTable AS tmp) AS src PIVOT (MAX(column_value)
FOR COLUMN_NAME in ([TrnYear], [TrnMonth], [EntryDate], [TrnTime], [StockCode], [Warehouse])) AS piv

SQL Server 2016: Turn multiple lines into columns with the different iterations

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

SQL Server Query for Many to Many Relationship - how to query?

This is an update to my previous question : Many to many relationship
The previous solution works fine, but now I want tgo improve the results a little bit. I´d like to have all the wavelength values in one row.
So instead of the following result :
DateTimeID Wavelength SensorID
11435 1581,665 334
11435 1515,166 334
11435 1518,286 335
I'd like to have something similar to this:
DateTimeID Wavelength1 Wavelength2 SensorID
11435 1581,665 1515,166 334
11435 1518,286 335
You could use the following which applies a row_number() to the records:
select DateTimeID,
[1] as Wavelength1,
[2] as Wavelength2,
SensorId
from
(
select [DateTimeID], [Wavelength], [SensorID],
row_number() over(partition by DateTimeID, SensorId
order by DateTimeID) rn
from yourtable
) src
pivot
(
max(Wavelength)
for rn in ([1], [2])
) piv
See SQL Fiddle with Demo.
If you will have an unknown number of wavelength values, then you can use dynamic SQL to generate this:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME('Wavelength'+cast(rn as varchar(50)))
from
(
select row_number() over(partition by DateTimeID, SensorId
order by DateTimeID) rn
from yourtable
) src
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT DateTimeID,' + #cols + ', SensorId from
(
select [DateTimeID], [Wavelength], [SensorID],
''Wavelength''+cast(row_number() over(partition by DateTimeID, SensorId
order by DateTimeID) as varchar(50)) rn
from yourtable
) x
pivot
(
max(Wavelength)
for rn in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo