使用 PIVOT 和 UNPIVOT
您可以使用 PIVOT 和 UNPIVOT 關係運算子,將資料表值運算式變更為另一個資料表。PIVOT 會將運算式內一個資料行中的唯一值轉成輸出中的多個資料行,以旋轉資料表值運算式,然後依據最終輸出的需要,對其餘的任何資料行值執行必要的彙總。UNPIVOT 執行的作業則與 PIVOT 相反,它會將資料表值運算式旋轉為資料行值。
[!附註]
針對升級至 SQL Server 2005 或更新版本的資料庫使用 PIVOT 與 UNPIVOT 時,資料庫的相容性層級必須設為 90 或更高。如需有關如何設定資料庫相容性層級的詳細資訊,請參閱<sp_dbcmptlevel (Transact-SQL)>。
PIVOT 提供的語法比您另外指定一連串複雜的 SELECT...CASE 陳述式,還要簡單易讀。如需 PIVOT 語法的完整描述,請參閱<FROM (Transact-SQL)>。
下列是註解的 PIVOT 語法。
SELECT <non-pivoted column>,
[first pivoted column] AS <column name>,
[second pivoted column] AS <column name>,
...
[last pivoted column] AS <column name>
FROM
(<SELECT query that produces the data>)
AS <alias for the source query>
PIVOT
(
<aggregation function>(<column being aggregated>)
FOR
[<column that contains the values that will become column headers>]
IN ( [first pivoted column], [second pivoted column],
... [last pivoted column])
) AS <alias for the pivot table>
<optional ORDER BY clause>;
基本 PIVOT 範例
下列程式碼範例會產生包含兩個資料行的資料表,其中有四個資料列。
USE AdventureWorks ;
GO
SELECT DaysToManufacture, AVG(StandardCost) AS AverageCost
FROM Production.Product
GROUP BY DaysToManufacture;
以下為結果集:
DaysToManufacture AverageCost
0 5.0885
1 223.88
2 359.1082
4 949.4105
沒有任何產品是定義為三天內完工 (DaysToManufacture)。
下列程式碼會顯示同樣的結果,但是經過樞紐處理後,讓 DaysToManufacture 值變成了資料行的標題。即使結果為 NULL,還是為這三 [3] 天產生了一個資料行。
-- 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;
以下為結果集:
Cost_Sorted_By_Production_Days 0 1 2 3 4
AverageCost 5.0885 223.88 359.1082 NULL 949.4105
複雜 PIVOT 範例
當您想要產生跨表格式報表來建立資料摘要時,這個常見的狀況可以顯出 PIVOT 的用處。例如,假設您想要查詢 AdventureWorks 範例資料庫中的 PurchaseOrderHeader 資料表,以判斷某些員工所下的訂單數目。下列查詢會提供這個報表,並依供應商排序:
USE AdventureWorks;
GO
SELECT VendorID, [164] AS Emp1, [198] AS Emp2, [223] AS Emp3, [231] AS Emp4, [233] AS Emp5
FROM
(SELECT PurchaseOrderID, EmployeeID, VendorID
FROM Purchasing.PurchaseOrderHeader) p
PIVOT
(
COUNT (PurchaseOrderID)
FOR EmployeeID IN
( [164], [198], [223], [231], [233] )
) AS pvt
ORDER BY pvt.VendorID;
以下為部分結果集。
VendorID Emp1 Emp2 Emp3 Emp4 Emp5
1 4 3 5 4 4
2 4 1 5 5 5
3 4 3 5 4 4
4 4 2 5 5 4
5 5 1 5 5 5
這個子選擇陳述式所傳回的結果,是根據 EmployeeID 資料行進行樞紐處理而來。
SELECT PurchaseOrderID, EmployeeID, VendorID
FROM PurchaseOrderHeader;
這表示由 EmployeeID 資料行所傳回的唯一值本身會變成最終結果集中的欄位。因此,樞紐子句指定的每個 EmployeeID 編號都會有一個資料行:在此情況下,是以下員工 164、198、223、231 和 233。PurchaseOrderID 資料行會當作數值資料行,這是在最終輸出中傳回的資料行 (稱為群組資料行) 所根據以進行分組的資料行。在此情況下,COUNT 函數會對群組資料行進行彙總。請注意,此時會顯示警告訊息,指出計算每個員工的 COUNT 時,並未考慮 PurchaseOrderID 資料行中出現的任何 NULL 值。
![]() |
---|
使用彙總函數搭配 PIVOT,在計算彙總時不會考慮值資料行中出現的任何 NULL 值。 |
UNPIVOT 執行的作業則幾乎與 PIVOT 完全相反,它會將資料行旋轉成資料列。假設上述範例中所產生的資料表在資料庫中是儲存為 pvt,而現在您想要將資料行識別碼 Emp1、Emp2、Emp3、Emp4 和 Emp5 旋轉成對應到特定供應商的資料列值。這表示您必須識別兩個額外的資料行。包含所要旋轉的資料行值 (Emp1、Emp2、...) 的資料行將命名為 Employee,而保留目前位在所要旋轉資料行之下的值的資料行則命名為 Orders。在 Transact-SQL 定義中,這些資料行會分別對應到 pivot_column 和 value_column。查詢內容如下。
--Create the table and insert values as portrayed in the previous example.
CREATE TABLE pvt (VendorID int, Emp1 int, Emp2 int,
Emp3 int, Emp4 int, Emp5 int);
GO
INSERT INTO pvt VALUES (1,4,3,5,4,4);
INSERT INTO pvt VALUES (2,4,1,5,5,5);
INSERT INTO pvt VALUES (3,4,3,5,4,4);
INSERT INTO pvt VALUES (4,4,2,5,5,4);
INSERT INTO pvt VALUES (5,5,1,5,5,5);
GO
--Unpivot the table.
SELECT VendorID, Employee, Orders
FROM
(SELECT VendorID, Emp1, Emp2, Emp3, Emp4, Emp5
FROM pvt) p
UNPIVOT
(Orders FOR Employee IN
(Emp1, Emp2, Emp3, Emp4, Emp5)
)AS unpvt;
GO
以下為部分結果集。
VendorID Employee Orders
1 Emp1 4
1 Emp2 3
1 Emp3 5
1 Emp4 4
1 Emp5 4
2 Emp1 4
2 Emp2 1
2 Emp3 5
2 Emp4 5
2 Emp5 5
...
請注意,UNPIVOT 並非與 PIVOT 完全相反。PIVOT 會執行彙總,因此它會將多個可能的資料列合併成輸出中的單一資料列。因為資料列已經合併,所以 UNPIVOT 所產生的並不是原始的資料表值運算式結果。此外,UNPIVOT 輸入中的 NULL 值會在輸出中消失不見,但是在執行 PIVOT 作業之前,輸入中原本可能有 NULL 值。
AdventureWorks 範例資料庫中的 Sales.vSalesPersonSalesByFiscalYears 檢視使用了 PIVOT 來傳回每位銷售人員在每個會計年度的總銷售額。若要在 SQL Server Management Studio 中編寫檢視的指令碼,請在 [物件總管] 中找出 [AdventureWorks] 資料庫 [檢視] 資料夾下的檢視。以滑鼠右鍵按一下檢視名稱,然後選取 [編寫檢視的指令碼為]。