In one of my previous post I showed you how to UNPIVOT multiple columns. On similar lines I also wanted to write on “How to PIVOT multiple columns?”, so this post was due for some time. Actually I was looking for some efficient way of doing it. Limitation of PIVOT operator is, it supports pivoting only on a single column. But you can always have multiple PIVOT operators in the FROM clause. I was trying to create a PIVOT query with multiple columns with multiple PIVOT operators. But at the end of it I found that our old fashioned CASE expression is performing much better than a multiple PIVOT operator query.
Even though I’m writing this post on how to write a multiple PIVOT operator query, my suggestion is use CASE expressions instead for getting better performance. Though personally I like to avoid CASE also. Normally I like to do it in Reporting Services, by creating a Matrix report. Now a days almost all Reporting Tools provides you an option of creating Matrix report. And good thing about Matrix report is unlike PIVOT operator you don’t need to hard code any column value.
If you try to write a PIVOT query with 2 PIVOT operators, and use same column in FOR clause you will get an error : Invalid column name NameOfColumn.
Or if you use same column, but by declaring it again and using a different alias name, you still get an error : The column name ValueOfColumn specified in the PIVOT operator conflicts with the existing column name in the PIVOT argument.
So what’s the solution? Solution is, declare the same column again, change the values in the column by some constant(you can add some constant, or you can concat some identifier ) and assign a new alias name to column.
Lets see the following example, I have used the AdventureWorks database of SQL Server 2005.
USE AdventureWorks |
The query returns total quantity and line amount for year 2001 and 2002 for the product id 771 for all customers. If look at the query carefully in Main sub query, CONVERT(VARCHAR(4),H.OrderDate,120) this convert statement will take out the Year part from the OrderDate column. I have declared the same column twice, at first I concatenated Q to the Year, and at second time I concatenated the V. Just execute the Main sub query, so it will be easy to understand for you.
SELECT H.CustomerId, SUM(D.OrderQty) AS TotalQty, SUM(D.LineTotal) AS TotalVal, 'Q'+CONVERT(VARCHAR(4),H.OrderDate,120) AS QYear, 'V'+CONVERT(VARCHAR(4),H.OrderDate,120) AS VYear FROM Sales.SalesOrderDetail AS D INNER JOIN Sales.SalesOrderHeader AS H ON D.SalesOrderId = H.SalesOrderId WHERE D.ProductId=771 AND H.OrderDate >='20010101' AND H.OrderDate <'20030101' GROUP BY H.CustomerId, CONVERT(VARCHAR(4),H.OrderDate,120) |
Now we have 2 columns, with different values, and we can use them in different PIVOT with same effect, and that’s what I have done in my 1st query.
Here is a CASE expression version of same query, which gives much better performance if you scale it for large amount data.
USE AdventureWorks |
You can test the performance of both queries. If you want to scale it for larger data you can remove the WHERE conditions added by me. Total execution time for CASE query is almost half to that of PIVOT query.