不同於CROSS JOIN, CROSS APPLY, OUTER APPLY,MSDN文檔對PIVOT和UNPIVOT 想得重視了一點,單獨做了一個頁面來介紹。
簡單來說,PIVOT用來把行轉成列,而UNPIVOT可以把列轉成行。
用MSDN文檔給出的兩個例子來做說明。
例一,基礎示例。
-- Pivot table with one row and five columns
SELECT 'AverageCost' AS Cost_Sorted_By_Production_Days,
[0], [1], [2], [3], [4]
FROM
(SELECT DaysToManufacture, StandardCost
FROM Production.Product) AS SourceTable
PIVOT
(
AVG(StandardCost)
FOR DaysToManufacture IN ([0], [1], [2], [3], [4])
) AS PivotTable;
例二,複雜示例。
USE AdventureWorks2014;
GO
SELECT VendorID, [250] AS Emp1, [251] AS Emp2, [256] AS Emp3, [257] AS Emp4, [260] AS Emp5
FROM
(SELECT PurchaseOrderID, EmployeeID, VendorID
FROM Purchasing.PurchaseOrderHeader) p
PIVOT
(
COUNT (PurchaseOrderID)
FOR EmployeeID IN
( [250], [251], [256], [257], [260] )
) AS pvt
ORDER BY pvt.VendorID;
第一步,也是所有SELECT語句的第一步,弄清楚Source Table,即FROM後面的源table。
對於例一:
(SELECT DaysToManufacture, StandardCost
FROM Production.Product) AS SourceTable
對於例二:
(SELECT PurchaseOrderID, EmployeeID, VendorID
FROM Purchasing.PurchaseOrderHeader) p
第二步,理解PIVOT的作用域。
對於例一,即針對DaysToManufacture在IN範疇中的每個值計算AVG(StandardCost)
(
AVG(StandardCost)
FOR DaysToManufacture IN ([0], [1], [2], [3], [4])
)
對於例二,即針對EmployID在IN範疇中的每一個值計算COUNT (PurchaseOrderID):
(
COUNT (PurchaseOrderID)
FOR EmployeeID IN
( [250], [251], [256], [257], [260] )
)
事實上,可以用等價的SQL來實現PIVOT。以下兩段SQL是等價的:
SELECT empid, [2013], [2014], [2015]
FROM ( SELECT empid, YEAR(orderdate) AS orderyear, val
FROM Sales.OrderValues ) AS D
PIVOT( SUM(val) FOR orderyear IN([2013],[2014],[2015]) ) AS P;
SELECT empid,
SUM(CASE WHEN orderyear = 2013 THEN val END) AS [2013],
SUM(CASE WHEN orderyear = 2014 THEN val END) AS [2014],
SUM(CASE WHEN orderyear = 2015 THEN val END) AS [2015]
FROM ( SELECT empid, YEAR(orderdate) AS orderyear, val
FROM Sales.OrderValues ) AS D
GROUP BY empid;
UNPIVOT執行的是PIVOT相反但原理完全一致的操作。
然而,有兩個問題比較困擾我:
- 如何執行動態的PIVOT?比如,示例中的IN()部分都是寫死的,現實中顯然這種固定值的情況不多。搜索了一圈下來,答案几乎全都是手動拼接IN後面的字符串。有一篇Blog值得一看。
- 這個功能在現實中的意義?貌似這個沒有標準答案。無論如何,行列互轉總是個噱頭。
是爲之記。
Alva Chien
2016.6.14