Dynamic Pivot in TSQL
Making 'dynamic' even more dynamic
In this month’s SQL Server Magazine (11/2009) an article was featured on the concept of dynamic pivot. This was of interest to me because, since I peruse the stats on how people arrive at this site, I have seen several searches from Google! looking for dynamic pivot examples. Truth be told, I haven’t run into a situation to dynamically pivot anything in a production environment, so I’m not sure what all of the drooling is all about. But, since there is a demand, I thought I’d throw my .02 cents in, and I’ll use SQLMag’s example as my base reference.
The author did a good job with the write-up - my hat is off to him. However, glancing at the article one can see that concept could be carried further…very little is “dynamic” about his code, so l invite you to look at it and mine and decide for yourself. First, let’s set up the tables and data. I have included both his [1] DDL as well as that from another blog posting elsewhere on this site (to demonstrate that it will work in a dynamic fashion, all things being equal).
DROP TABLE DemoTable
GO
CREATE TABLE DemoTable
(
AssignmentName VARCHAR(255),
StudentName VARCHAR(255),
Grade INT
)
GO
INSERT INTO DemoTable
Values('Assignment1', 'John Smith', 70)
INSERT INTO DemoTable
Values('Assignment1', 'Jane Smith', 80)
INSERT INTO DemoTable
Values('Assignment1', 'Paul Smith', 75)
INSERT INTO DemoTable
Values('Assignment2', 'John Smith', 50)
INSERT INTO DemoTable
Values('Assignment2', 'Paul Smith', 65)
INSERT INTO DemoTable
Values('Assignment3', 'Jane Smith', 70)
GO
DROP TABLE PlantMetrics;
GO
CREATE TABLE PlantMetrics
(PlantID int
,TransDt datetime
,Metric char (4)
,Value nvarchar (10)
,CONSTRAINT pk_plmt PRIMARY KEY CLUSTERED (PlantID, TransDt, Metric)
);
GO
INSERT PlantMetrics
VALUES (1,'7/1/09', 'MHRS', '24')
,(1,'7/1/09', 'WTHR', 'Sunny')
,(1,'7/1/09', 'TEMP', '88')
,(2,'7/1/09', 'TEMP', '94')
,(3,'7/2/09', 'DOWN', '7')
GO
Below is his code [1]:
DECLARE @SQL as VARCHAR (MAX)
DECLARE @Columns AS VARCHAR (MAX)
SELECT @Columns=
COALESCE(@Columns + ',','') + QUOTENAME(AssignmentName)
FROM
(
SELECT DISTINCT AssignmentName
From DemoTable
) AS B
ORDER BY B.AssignmentName
SET @SQL='
WITH PivotData AS
(
SELECT
AssignmentName,
StudentName,
Grade
FROM DemoTable
)
SELECT
StudentName,
' + @Columns + '
FROM PivotData
PIVOT
(
SUM(Grade)
FOR AssignmentName
IN (' + @Columns + ')
) AS PivotResult
ORDER BY StudentName'
EXEC (@SQL)
A few things to point out: don’t use SUM for the aggregate. MIN or MAX works better, because sometimes the value that is the subject of the pivot is not an integral (integer, numeric, etc.) type. Also, I see some columns that could be replaced here to make the entire code dynamic. Finally, here is my code:
DECLARE @SQL as VARCHAR(MAX)
DECLARE @NewColumnList AS VARCHAR(MAX)
DECLARE @table table (SourceColumnNames varchar (100))
DECLARE @TargetTable varchar (100)
-- Column names
DECLARE @PivotColumn varchar (100)
DECLARE @AnchorColumn varchar (100)
-- Pivot
DECLARE @PivotValue varchar (100)
DECLARE @Operation char (3)
SET @TargetTable = 'DemoTable'
SET @PivotColumn = 'AssignmentName'
SET @AnchorColumn = 'StudentName'
SET @PivotValue = 'Grade'
SET @Operation = 'MAX'
INSERT @table
EXEC ('SELECT ' + @PivotColumn + ' FROM ' + @TargetTable )
SELECT @NewColumnList = COALESCE(@NewColumnList + ', ','') + QUOTENAME(SourceColumnNames)
FROM
(
SELECT DISTINCT SourceColumnNames
FROM @table
) AS B
SET @SQL = '
WITH PivotData AS
(
SELECT
' + @PivotColumn + ',
' + @AnchorColumn + ',
' + @PivotValue + '
FROM ' + @TargetTable+ '
)
SELECT
' + @AnchorColumn + ',
' + @NewColumnList + '
FROM PivotData
PIVOT
(
'+@Operation+'(' + @PivotValue + ')
FOR ' + @PivotColumn + '
IN (' + @NewColumnList + ')
) AS PivotResult
ORDER BY ' + @AnchorColumn + ''
EXEC (@SQL)
GO
You can see that there is nothing terribly difficult or complicated about this. Change up a few things, add a few variables, and we’re set! Notice my naming convention for the variables used to help in understanding this thing. I named these for no reason other than they seemed to make sense to me, considering Books Online didn’t attempt to help out much with the components of the syntax. Here’s the skinny on the var names:
· @TargetTable – the name of the dynamic table
· @PivotColumn –the row name that will be used to generate columns in the pivot
· @AnchorColumn – the column that stays put and is unchanged in its’ purpose in life
· @PivotValue –the metric that in the pivot table
· @Operation – probably shouldn’t have even used this, could have hard coded it MAX or MIN because sometimes values can’t be summed or averaged.
In closing, do we need ‘dynamic’ pivot (or unpivot for that matter)? Not sure, but I know that I haven’t needed it in production for either of my past two clients. Who knows…maybe someone can use something like this; as always let me know.
Happy dynamic pivoting!
Lee
-----------------------------
If I start walking to Las Vegas tonight, I think I can make SQLServerConnections before its over
[1] Sellers, M. (2009). Pivoting the Dynamic Way. SQL Server Magazine, November 2009. Retrieved 11/6/2009 from http://www.sqlmag.com/Article/ArticleID/102722/Pivoting_the_Dynamic_Way.html