Using pivot and then counting the values in each pivot column - sql-server

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

Related

Created following query, Use 2 pivot for income and profit,But want order like 1 st column of profit then 1 st column of income

PIVOT Query:
Declare #cols nvarchar(max)
SELECT #cols= COALESCE (#cols+',['+shipmentmonth+' Profit]','['+shipmentmonth+' Profit]') from (select distinct shipmentmonth from MargaritaSales where ShipYearMonth >='201601' ) P
Declare #cols1 nvarchar(max)
SELECT #cols1= COALESCE (#cols1+',['+shipmentmonth+' Income]','['+shipmentmonth+' Income]') from (select distinct shipmentmonth from MargaritaSales where ShipYearMonth >='201601' ) P1
--Declare #cols nvarchar(max)
--SELECT #cols= COALESCE (#cols+',['+shipmentmonth+' Profit]'+'['+shipmentmonth+' Income]','['+shipmentmonth+' Profit]'+'['+shipmentmonth+' Income]') from (select distinct shipmentmonth from MargaritaSales where ShipYearMonth >='201601' ) P
--select #cols
DECLARE #query NVARCHAR(MAX)
SET #query =
'SELECT *
FROM ( SELECT T.buyer,T.buyerid, T.shipmentmonth,T.shipmentmonth+
''Income'' as shipmentmonth1,
(t.PROFIT +t.USDServiceIncome -t.USDServiceExpense) as Profit1,
( t.Income+t.OfficeIncome+t.USDServiceIncome) as Income1
FROM MargaritaSales T where T.ShipYearMonth >=''201601'') up PIVOT
(sum(PROFIT1)
FOR shipmentmonth IN ('+ #cols +')) AS pvt1
PIVOT (sum(Income1)
FOR shipmentmonth1 IN (+ #cols1 + ')) AS pvt2'
EXEC SP_EXECUTESQL #query
it is easier to use CASE STATEMENT for such case rather than PIVOT
this is how it is looks like. You need to convert to Dynamic Query as per what you did with PIVOTing
SELECT T.buyer, T.buyerid,
SUM(CASE WHEN ShipYearMonth = 201601 THEN t.PROFIT + t.USDServiceIncome -t.USDServiceExpense END) as Profit1,
SUM(CASE WHEN ShipYearMonth = 201601 THEN t.Income + t.OfficeIncome + t.USDServiceIncome) as Income1,
SUM(CASE WHEN ShipYearMonth = 201602 THEN t.PROFIT + t.USDServiceIncome -t.USDServiceExpense END) as Profit2,
SUM(CASE WHEN ShipYearMonth = 201602 THEN t.Income + t.OfficeIncome + t.USDServiceIncome) as Income2
FROM MargaritaSales T
WHERE T.ShipYearMonth >= 201601
GROUP BY T.buyer, T.buyerid

How to pivot data in T-SQL for 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

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

Aggregation in dynamic pivot table in SQL Server 2012

I have a table in SQL Server 2012 which has 5 million rows.
Table view is like this :
CustomerID ProdID FavouriteProduct
1 A A
1 A A
1 A A
1 B A
1 A A
1 A A
1 A A
1 B A
2 A C
2 AN C
2 G C
2 C C
2 C C
2 F C
2 D C
2 C C
As you can see there are so many different products.
I already wrote a query for that :
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
--Get distinct values of the PIVOT Column
SELECT
#ColumnName = ISNULL(#ColumnName + ',','') + QUOTENAME(prodID)
FROM
(SELECT DISTINCT ProdID FROM Table) AS Prods
--Prepare the PIVOT query using the dynamic
SET #DynamicPivotQuery =
N'SELECT CustomerID, ' + #ColumnName + '
FROM table
PIVOT(count(CustomerID)
FOR ProdID IN (' + #ColumnName + ')) AS PVTTable'
--Execute the Dynamic Pivot Query
EXEC sp_executesql #DynamicPivotQuery
Normally, I expect count(*) or Count(SubID) would count the number of each products bought by each customer, but it doesn't. It says
Invalid Column name CustomerID
instead.
You can't count the CustomerId while selecting it, try this instead:
SET #DynamicPivotQuery =
N'SELECT CustomerID, ' + #ColumnName + '
FROM table
PIVOT(count(FavouriteProduct)
FOR ProdID IN (' + #ColumnName + ')) AS PVTTable'
You need a column to count, and CustomerId is not allowed since you are selecting it. If FavouriteProduct fails, I suggest you fake a column or find another column.
Since you have more columns than described, I modified query. Try this instead:
SET #DynamicPivotQuery =
N'SELECT CustomerID, ' + #ColumnName + '
FROM
(SELECT CustomerID, FavouriteProduct, ProdID FROM table) x
PIVOT(count(FavouriteProduct)
FOR ProdID IN (' + #ColumnName + ')) AS PVTTable'

Resources