This question already has answers here:
SQL Server dynamic PIVOT query?
(9 answers)
Closed 7 years ago.
I need to dynamically transform data in table #table from this format:
spot_id name pct
------- ---- ---
1 A 2
1 B 8
1 C 6
2 A 4
2 B 5
3 A 5
3 D 1
3 E 4
to:
spot_id A B C D E
------- --- --- --- --- ---
1 2 5 6 0 0
2 4 5 0 0 0
3 5 0 0 1 4
The thing is that I don't know in advance what the values of column "name" are or how many of them there are, so I think that I have to use some kind of dynamic SQL pivoting
Just figured out how to solve the problem:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(name)
from (SELECT DISTINCT name FROM #table) T
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = N'SELECT spot_id,' + #cols + N' from
(
select spot_id, name, pct
from #table
) as x
pivot
(
max(pct)
for name in (' + #cols + N')
) as p '
exec sp_executesql #query;
If there more elegant way to do the same?
declare #t table (Spot int,name varchar(1),pct int)
insert into #t(Spot,name,pct)values(1,'A',2),(1,'B',8),(1,'C',6),(2,'A',4),(2,'B',5),(3,'A',5),(3,'D',1),(3,'E',4)
select Spot,ISNULL([A],0)[A],ISNULL([B],0)[B],ISNULL([C],0)[C],ISNULL([D],0)[D],ISNULL([E],0)[E] from (Select Spot,PCT,Name from #t)T
PIVOT(MAX(PCT) FOR Name IN ([A],[B],[C],[D],[E]))P
select Spot,ISNULL(MAX(CASE WHEN name = 'A' THEN pct END),0)A,ISNULL(MAX(CASE WHEN name = 'B' THEN pct END),0)B ,
ISNULL(MAX(CASE WHEN name = 'C' THEN pct END),0)C ,
ISNULL(MAX(CASE WHEN name = 'D' THEN pct END),0)D ,ISNULL(MAX(CASE WHEN name = 'E' THEN pct END),0)E from #t
GROUP BY Spot
Using Dynamic Query :
if object_id('tempdb..#t') is not null
drop table #t
CREATE table #t(Spot int,name varchar(1),pct int)
insert into #t(Spot,name,pct)values(1,'A',2),(1,'B',8),(1,'C',6),(2,'A',4),(2,'B',5),(3,'A',5),(3,'D',1),(3,'E',4)
DECLARE #statement NVARCHAR(max)
,#columns NVARCHAR(max)
SELECT #columns = ISNULL(#columns + ', ', '') + N'[' + tbl.name + ']'
FROM (
SELECT DISTINCT name
FROM #t
) AS tbl
SELECT #statement = ' select Spot,ISNULL([A],0)[A],ISNULL([B],0)[B],ISNULL([C],0)[C],ISNULL([D],0)[D],ISNULL([E],0)[E] from (Select Spot,PCT,Name from #t)T
PIVOT(MAX(PCT) FOR Name IN (' + #columns + ')) as pvt'
EXEC sp_executesql #statement = #statement
Related
I have a table that looks like this:
Agent_id
break_id
time
1
1
15
1
2
12
1
2
12
I used pivot to get this structure:
Agent_id
1
2
1
15
24
The problem is that I need to get the count for the pivoted columns, in the example I need to have a structure like this:
Agent_id
1
2
count1
count2
1
15
24
1
2
And I'm not sure on how to do it ... this is the query so far.
DECLARE #COLUMNS VARCHAR(MAX)
DECLARE #QUERY nVARCHAR(MAX)
SELECT #COLUMNS = COALESCE(#COLUMNS + ', ','') + QUOTENAME([break_id])
FROM
(SELECT DISTINCT [break_id] FROM test) AS B
ORDER BY B.[break_id]
SET #QUERY = '
SELECT agent_id,
'+#COLUMNS+'
FROM (
SELECT TOP (1000)
agent_id,break_id,time_inbreak
FROM test
) as pivotData
PIVOT (
SUM(time_inbreak)
FOR break_id IN ('+#COLUMNS+')
) as pivotResult
'
EXEC sp_executesql #QUERY
Any help is greatly appreciated
Unfortunately, PIVOT can only pivot a single column. But we can do multiple columns using conditional aggregation SUM(CASE WHEN... and COUNT(CASE WHEN...:
DECLARE #COLUMNS VARCHAR(MAX)
DECLARE #QUERY nVARCHAR(MAX)
SELECT #COLUMNS = COALESCE(#COLUMNS + ', ','') +
'Sum' + QUOTENAME([break_id]) +
' = SUM(CASE WHEN break_id = ' + break_id + ' THEN time_inbreak END), Count' +
QUOTENAME([break_id]) + ' = COUNT(CASE WHEN break_id = ' + break_id + ' THEN 1 END)'
FROM
(SELECT DISTINCT [break_id] FROM test) AS B
ORDER BY B.[break_id]
OPTION (MAXDOP 1);
SET #QUERY = '
SELECT agent_id,
'+#COLUMNS+'
FROM (
SELECT TOP (1000)
agent_id,break_id,time_inbreak
FROM test
) as pivotData
GROUP BY agent_id;
';
PRINT #QUERY
EXEC sp_executesql #QUERY
I must say, I'm not sure how safe it is to aggregate the columns like that, especially in the face of parallelism. Preferably use STRING_AGG or FOR XML PATH(''), TYPE. At the very least I have added OPTION (MAXDOP 1) to prevent parallelism
This question already has answers here:
Split and get second row as value [duplicate]
(3 answers)
Closed 4 years ago.
Current Result:
Data
-----
1#2#3#4#5#6
Expected Result:
Data1 Data2 Data3 Data4 Data5 Data6
----------------------------------------
1 2 3 4 5 6
Tried with SUBSTRING(), LEFT(), CHARINDEX() but didn't help.
Can anybody please tell, what I'm missing?
Try:
DECLARE #Var NVARCHAR(MAX)
SET #Var ='1#2#3#4#5#6'
DECLARE #XML AS XML
DECLARE #Delimiter AS CHAR(1) ='#'
SET #XML = CAST(('<X>'+REPLACE(#Var,#Delimiter ,'</X><X>')+'</X>') AS XML)
DECLARE #temp TABLE (ID INT,name NVARCHAR(MAX))
INSERT INTO #temp
SELECT N.value('.', 'INT') AS ID, 'data' + CONVERT(Nvarchar,N.value('.', 'INT')) as name
FROM #XML.nodes('X') AS T(N)
SELECT *
FROM #temp
PIVOT
(
MAX(id)
FOR name IN ([data1],[data2],[data3],[data4],[data5],[data6])
) PIV;
Result:
data1 data2 data3 data4 data5 data6
1 2 3 4 5 6
With Dynamic SQL:
SELECT *
INTO ##temp
FROM #temp
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SELECT #cols = STUFF((SELECT ',' + QUOTENAME(name)
from #temp
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query = N'SELECT *
FROM
##temp
PIVOT
(
MAX(id)
FOR name IN (' + #cols + N')
) PIV'
EXEC(#query)
DROP TABLE ##temp
Try this :
SELECT value FROM STRING_SPLIT('1#2#3#4#5#6', '#');
the result here
I could manage to make a dynamic pivot query in SQL Server as below:
DECLARE #sql nvarchar(max),
#columns nvarchar(max)
SELECT #columns = STUFF((SELECT DISTINCT ',' + QUOTENAME(nationality)
FROM NewComers
FOR XML PATH('')), 1, 1, '')
SELECT #sql = 'SELECT p.registrationdate, '+ #columns +
' FROM (SELECT s.registrationdate, s.Nationality FROM newcomers s) AS t
PIVOT (
COUNT(Nationality) FOR nationality IN (' + #columns+ ')) p'
EXEC (#sql)
My problem is that I want to add a column at the end of the result. So currently I get this result:
registrationdate GER TUR CAN AUS
------------------ ----- ----- ----- -----
16/11/2016 10 8 6 7
21/08/2020 4 5 3 2
08/04/2019 1 3 5 0
and I need to have this as result:
registrationdate GER TUR CAN AUS Total
------------------ ----- ----- ----- ----- -------
16/11/2016 10 8 6 7 31
21/08/2020 4 5 3 2 14
08/04/2019 1 3 5 0 9
I tried several solutions in Stack Overflow but I nothing quite worked.
Will appreciate your help :)
Can you add the Total as unioned rows with your newcomer table and then manually add the “Total” string to your #columns variable?
Basically create a CTE of newcomer table with a union which groups by your date and sums the total up and stick the word “Total” into the nationality column.
Then just add the Total into your #columns string to pivot it out.
Something like this? I don't have access to a SQL Server currently to test it.
DECLARE #sql nvarchar(max),
#columns nvarchar(max)
SELECT #columns = STUFF((SELECT DISTINCT ',' + QUOTENAME(nationality)
FROM NewComers
FOR XML PATH('')), 1, 1, '')
SELECT #sql = '
;with CTE as (
select registrationdate, Nationality, count(Nationality) as Cnt from newcomers group by registrationdate, Nationality
union all
select registrationdate, ''Total'', count(Nationality) as Cnt from newcomers group by registrationdate
)
SELECT p.registrationdate, '+ #columns + ', Total' +
' FROM (SELECT registrationdate, Nationality, Cnt FROM CTE) AS t
PIVOT (
max(Cnt) FOR nationality IN (' + #columns+ ',Total)) p'
EXEC (#sql)
This question already has answers here:
Efficiently convert rows to columns in sql server
(5 answers)
Closed 6 years ago.
I'm struggling with an error while making a dynamic pivot table
The source data is
JobID | SalesForMonth | YearMonth
7734 | 400 | 2016-12
7734 | 350 | 2017-01
8540 | 444 | 2016-12
8540 | 300 | 2017-01
and aiming for
JobID | 2016-12 | 2017-01
7734 | 400 | 350
8540 | 444 | 300
and I've tried to use a query I found on here to create the column headers. But must admit I don't really understand the 'For XML' line and getting a syntax error there on line 6
DECLARE
#cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SELECT #cols = STUFF((SELECT DISTINCT ',' + QUOTENAME(YearMonth)
FROM v_JobSalesByMonth
FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)') ,1,1,'')
SELECT #query =
'SELECT * FROM
(SELECT JobID, YearMonth, SalesForMonth
FROM v_JobSalesByMonth) X
PIVOT
(
(JobID, SalesForMonth)
for [YearMonth] in (' + #cols + ')
) P'
I'd also like to stick in a 'total sales' for the jobID column
Any help would be much appreciated
Declare #SQL varchar(max) = Stuff((Select Distinct ',' + QuoteName(YearMonth) From v_JobSalesByMonth Order by 1 For XML Path('')),1,1,'')
Select #SQL = '
Select [JobID],[TotalSales],' + #SQL + '
From (
Select JobID
,TotalSales = sum(SalesForMonth) over (Partition By JobID)
,YearMonth
,SalesForMonth
From v_JobSalesByMonth A
) A
Pivot (sum(SalesForMonth) For [YearMonth] in (' + #SQL + ') ) p'
Exec(#SQL);
Returns
EDIT - Dynamically Create View
Since you can't have dynamic SQL in a view, you could have job scheduled (daily or monthly) to drop and re-create the view.
if object_id('vw_SalesByMonth','v') is not null
drop view vw_SalesByMonth;
Declare #SQL varchar(max) = Stuff((Select Distinct ',' + QuoteName(YearMonth) From Yourtable Order by 1 For XML Path('')),1,1,'')
Select #SQL = '
Create View vw_SalesByMonth
AS
Select [JobID],[TotalSales],' + #SQL + '
From (
Select JobID
,TotalSales = sum(SalesForMonth) over (Partition By JobID)
,YearMonth
,SalesForMonth
From YourTable A
) A
Pivot (sum(SalesForMonth) For [YearMonth] in (' + #SQL + ') ) p'
Exec(#SQL);
Select * from vw_SalesByMonth
I have a table with the following structure where there are different types of results for a "SymbolQuoteID".
AnalysisResultID SymbolQuoteID ResultTypeID Result Updated
11 368 1 48.6750775191538 2015-10-08 22:09:15.680
12 368 2 47.7401046493826 2015-10-08 22:09:15.680
13 368 3 47.3978529584944 2015-10-08 22:09:15.680
14 368 4 43.4227483517635 2015-10-08 22:09:15.680
15 369 1 44.9316592945153 2015-10-08 22:09:15.680
16 369 2 48.8348167760945 2015-10-08 22:09:15.680
17 369 3 51.6463393199821 2015-10-08 22:09:15.680
18 369 4 51.7885923247485 2015-10-08 22:09:15.680
I need to SELECT the data so it is formatted like this where ResultTypeID is the column header and the corresponding Result column value is the column data. There is no aggregation, just the Result value. Also, the number of values for ResultTypeID will be consistent w/in a single query but may vary from query to query. For example, one time they might be 1 through 4 and another time 1 through 7, etc. Therefore, the number of columns in the pivoted table will change based upon the data.
SymbolQuoteID 1 2 3 4
368 48.6750775191538 47.7401046493826 47.3978529584944 43.4227483517635
369 44.9316592945153 48.8348167760945 51.6463393199821 51.7885923247485
It seems PIVOT might work but I've not used it before. Any help appreciated.
You can use dynamic PIVOT:
SQL Fiddle
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols =
STUFF((
SELECT DISTINCT ',' + QUOTENAME(ResultTypeID)
FROM tbl
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)')
, 1, 1, '')
SET #query =
'SELECT SymbolQuoteID, ' + #cols + '
FROM (
SELECT
SymbolQuoteID, ResultTypeID, Result
FROM tbl
)t
PIVOT
(
MAX(Result)
FOR ResultTypeID IN (' + #cols + ')
) p '
PRINT (#query)
EXEC (#query)
You can also do this using dynamic crosstab:
SQL Fiddle
DECLARE #maxResultTypeId INT
SELECT TOP 1 #maxResultTypeId = ResultTypeId FROM tbl ORDER BY ResultTypeID DESC
DECLARE #sql NVARCHAR(MAX) = ''
SELECT #sql =
'SELECT
SymbolQuoteID' + CHAR(10)
SELECT #sql = #sql +
' , MAX(CASE WHEN ResultTypeId = ' + CONVERT(VARCHAR(3), rn) + ' THEN Result END) AS ' + QUOTENAME(rn) + CHAR(10)
FROM(
SELECT TOP(#maxResultTypeId)
ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rn
FROM sys.columns
)t
SELECT #sql = #sql +
'FROM tbl
GROUP BY SymbolQuoteID'
PRINT (#sql)
EXEC (#sql)
SELECT * FROM
(
SELECT SymbolQuoteID
,ResultTypeID
,Result
FROM TableName ) t
PIVOT (SUM(Result)
FOR ResultTypeID
IN ([1],[2],[3],[4])
)p