How to pivot data in T-SQL for SQL Server - sql-server

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

Related

Using pivot and then counting the values in each pivot column

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

Add total column at the end, for a dynamic pivot SQL Server query

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)

Query to retrieve column values as rows from table which has duplicate rows in SQL Server 2008

I have a table which has records in the following format:
OrderID | CategoryID | Percentage
-----------------------------------------
Order1 1 50
Order1 2 100
Order1 3 70
Order2 1 85
Order2 2 75
Order2 3 100
Order3 1 50
Order3 2 60
Order3 3 80
Each order has multiple categories and each category has a percentage corresponding to that order.
I want to display it in the following format:
OrderID | Category1_% | Category2_% | Category3_%
-------------------------------------------------------
Order1 50 100 70
Order2 85 75 100
Order3 50 60 80
How do I write a query in SQL Server 2008 to retrieve percentages for each category of each (distinct) order? Please help.
Edit:
Orders table
ID | StartDate
--------------------
Order1 1/1/2016
Order2 10/1/2015
Order3 10/5/2016
The query which I executed is:
Declare #SQL varchar(max)
Declare #startDate datetime
set #startDate='1/1/2015'
Declare #endDate datetime
set #endDate= '10/1/2016'
Select #SQL = Stuff((Select Distinct ',' + QuoteName('Category'+cast(CategoryID as varchar(25))+'_%') From OrdersData Order By 1 For XML Path('')),1,1,'')
Select #SQL = 'Select [OrderID],' + #SQL +'
From (Select OrderID,Percentage,Item=''Category''+cast(CategoryID as varchar(25))+''_%'' from OrdersData join Orders on Orders.ID =OrdersData.OrderID
WHERE Orders.StartDate >= '+ ***convert(nvarchar(25), #startDate, 121)*** + ' and GCWR.StartDate <= '+ ***convert(nvarchar(25), #endDate, 121)*** +') A
Pivot (max(Percentage) For Item in (' + #SQL + ') ) p'
Exec(#SQL);
When I execute, I get an error 'Incorrect syntax near '00'.' How do I call the datetime values in the above query to display the results? Please let me know.
Just in case you need to go dynamic
Declare #SQL varchar(max)
Select #SQL = Stuff((Select Distinct ',' + QuoteName('Category'+cast(CategoryID as varchar(25))+'_%') From YourTable Order By 1 For XML Path('')),1,1,'')
Select #SQL = 'Select [OrderID],' + #SQL +'
From (Select OrderID,Percentage,Item=''Category''+cast(CategoryID as varchar(25))+''_%'' from YourTable) A
Pivot (max(Percentage) For Item in (' + #SQL + ') ) p'
Exec(#SQL);
Returns
Edit - Updated with StartDate
Declare #Date1 datetime = '2015-01-01'
Declare #Date2 datetime = '2016-10-01'
Declare #SQL varchar(max)
Select #SQL = Stuff((Select Distinct ',' + QuoteName('Category'+cast(CategoryID as varchar(25))+'_%') From YourTable Where cast(StartDate as date) between #Date1 and #Date2 Order By 1 For XML Path('')),1,1,'')
Select #SQL = 'Select [OrderID],' + #SQL +'
From (Select OrderID,Percentage,Item=''Category''+cast(CategoryID as varchar(25))+''_%'' from YourTable Where cast(StartDate as date) between '''+convert(varchar(10),#Date1,120)+''' and '''+convert(varchar(10),#Date2,120)+''') A
Pivot (max(Percentage) For Item in (' + #SQL + ') ) p'
Exec(#SQL);
Returns
Give this a try:
Select OrderID, [1] as [Category1_%], [2] as [Category2_%],[3] as [Category3_%]
From [TableName]
PIVOT (MAX([Percentage]) FOR [CategoryID] IN ([1],[2],[3])) myPivot
This assumes that you know all the possible values in the categoryID field and that each CategoryID an OrderID combination has a single percentage value.

sql server - transforming data with dynamic pivot [duplicate]

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

SQL Pivot Table Dynamic - exclude unused columns

I have the following SQL code that creates a very useful pivot table:
Use [A1_20132014]
DECLARE #cols NVARCHAR (MAX)
SELECT #cols = COALESCE (#cols + ',[' + Link_ID + ']', '[' + Link_ID + ']')
FROM (SELECT DISTINCT Link_ID FROM A1) PV
ORDER BY Link_ID
DECLARE #query NVARCHAR(MAX)
SET #query = 'SELECT * FROM
(
-- We will select the data that has to be shown for pivoting
SELECT date_1, StartHour,Cost, Link_ID
FROM A1
WHERE Section = (''1AB'')
) x
PIVOT
(
-- Values in each dynamic column
SUM(Cost)
-- Select columns from #cols
FOR Link_ID IN (' + #cols + ')
) p;'
EXEC SP_EXECUTESQL #query
from these headings
link_id Section date_1 StartHour Cost data_source
4000000027866016A 8NB 2013-09-02 6 5871 1
4000000027866017B 5EB 2013-10-09 9 8965 2
4000000027856512B 4TB 2013-05-06 15 6754 1
4000000027866015A 6HB 2013-06-08 8 5354 1
4000000027866011A 1AB 2013-06-09 11 2 1
with these source types;
link_Id nvarchar(50)
Section nvarchar(50)
Date_1 smalldatetime
StartHour int
Cost float
data_source int
However despite WHERE clause that specifies a certain section unfortunately ALL sections still appear in the pivot table but populated with NULL values all the way down.
Is there a way of completely excluding the columns that do not meet the WHERE clause?
Thanks for any help.
Henry.
Put the where clause in the subquery. That way you'll only get columns that apply to 1AB
SELECT #cols = COALESCE (#cols + ',[' + Link_ID + ']', '[' + Link_ID + ']')
FROM (SELECT DISTINCT Link_ID FROM A1 WHERE Section = '1AB') PV
ORDER BY Link_ID

Resources