行列互轉 Pivot 和 Unpivot

Pivot

如您所知,關係表是表格化的,即,它們以列-值對的形式出現

CUST_ID STATE_CODE TIMES_PURCHASED
------- ---------- ---------------
      1 CT                       1
      2 NY                      10
      3 NJ                       2
      4 NY                       4

select * from (
   select times_purchased, state_code
   from customers t
)
pivot 
(
   count(state_code)
   for state_code in ('NY','CT','NJ','FL','MO')
)
order by times_purchased
/

輸出如下: 

'NY'       'CT'       'NJ'       'FL'       'MO'
---------- ---------- ----------

10         1            2       ....

Unpivot

就像有物質就有反物質一樣,有 pivot 就應該有“unpivot”,

原查詢:

SELECT [Years],[Months],[Type],[I],[II],[III],[VI],[Total]
  FROM [ITShare].[dbo].[IT_HardWare_Month]

 

行轉列:

SELECT Years,Months,[Type],val,(case Sort when 'I' then '優' when 'II' then '良' when 'III' then '中' else '差' end) Sort,Sort as Sort1,Total
  FROM [IT_HardWare_Month]  
  unpivot(val for Sort in(I,II,III,VI)) as b

 

MSDN

http://msdn.microsoft.com/zh-cn/library/ms177410.aspx

使用 PIVOT 和 UNPIVOT

 

可以使用 PIVOT 和 UNPIVOT 關係運算符將表值表達式更改爲另一個表。PIVOT 通過將表達式某一列中的唯一值轉換爲輸出中的多個列來旋轉表值表達式,並在必要時對最終輸出中所需的任何其餘列值執行聚合。UNPIVOT 與 PIVOT 執行相反的操作,將表值表達式的列轉換爲列值。

ms177410.note(zh-cn,SQL.105).gif注意:
對升級到 SQL Server 2005 或更高版本的數據庫使用 PIVOT 和 UNPIVOT 時,必須將數據庫的兼容級別設置爲 90 或更高。有關如何設置數據庫兼容級別的信息,請參閱 sp_dbcmptlevel (Transact-SQL)

 

 

PIVOT 提供的語法比一系列複雜的 SELECT...CASE 語句中所指定的語法更簡單和更具可讀性。有關 PIVOT 語法的完整說明,請參閱 FROM (Transact-SQL)

以下是帶批註的 PIVOT 語法。

SELECT <非透視的列>,

    [第一個透視的列] AS <列名稱>,

    [第二個透視的列] AS <列名稱>,

    ...

    [最後一個透視的列] AS <列名稱>,

FROM

    (<生成數據的 SELECT 查詢>)

    AS <源查詢的別名>

PIVOT

(

    <聚合函數>(<要聚合的列>)

FOR

[<包含要成爲列標題的值的列>]

    IN ( [第一個透視的列], [第二個透視的列],

    ... [最後一個透視的列])

) AS <透視表的別名>

<可選的 ORDER BY 子句>;

簡單 PIVOT 示例

下面的代碼示例生成一個兩列四行的表。

USE AdventureWorks2008R2 ;
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 爲 3 的產品。

以下代碼顯示相同的結果,該結果經過透視以使 DaysToManufacture 值成爲列標題。提供一個列表示三 [3] 天,即使結果爲 NULL

-- 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 的常見情況是:需要生成交叉表格報表以彙總數據。例如,假設需要在 AdventureWorks2008R2 示例數據庫中查詢 PurchaseOrderHeader 表以確定由某些特定僱員所下的採購訂單數。以下查詢提供了此報表(按供應商排序)。

USE AdventureWorks2008R2;
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;

以下爲部分結果集。

VendorID    Emp1        Emp2        Emp3        Emp4        Emp5

1492        2           5           4           4           4

1494        2           5           4           5           4

1496        2           4           4           5           5

1498        2           5           4           4           4

1500        3           4           4           5           4

將在 EmployeeID 列上透視此嵌套 select 語句返回的結果。

SELECT PurchaseOrderID, EmployeeID, VendorID
FROM PurchaseOrderHeader;

這意味着 EmployeeID 列返回的唯一值自行變成了最終結果集中的字段。因此,在透視子句中指定的每個 EmployeeID 號都有相應的一列:在本例中爲僱員 164198223231233PurchaseOrderID 列作爲值列,將根據此列對最終輸出中返回的列(稱爲分組列)進行分組。在本例中,通過 COUNT 函數聚合分組列。請注意,將顯示一條警告消息,指出爲每個僱員計算 COUNT 時未考慮顯示在 PurchaseOrderID 列中的任何空值。

ms177410.note(zh-cn,SQL.105).gif重要提示:
如果聚合函數與 PIVOT 一起使用,則計算聚合時將不考慮出現在值列中的任何空值。

 

 

UNPIVOT 將與 PIVOT 執行幾乎完全相反的操作,將列轉換爲行。假設以上示例中生成的表在數據庫中存儲爲 pvt,並且您需要將列標識符 Emp1Emp2Emp3Emp4Emp5 旋轉爲對應於特定供應商的行值。這意味着必須標識另外兩個列。包含要旋轉的列值(Emp1Emp2...)的列將被稱爲 Employee,將保存當前位於待旋轉列下的值的列被稱爲 Orders。這些列分別對應於 Transact-SQL 定義中的 pivot_columnvalue_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 的輸入中的空值不會顯示在輸出中,而在執行 PIVOT 操作之前,輸入中可能有原始的空值。

AdventureWorks2008R2 示例數據庫中的 Sales.vSalesPersonSalesByFiscalYears 視圖將使用 PIVOT 返回每個銷售人員在每個會計年度的總銷售額。若要在 SQL Server Management Studio 中編寫視圖腳本,請在“對象資源管理器”中,在“視圖”文件夾下找到 AdventureWorks2008R2 數據庫對應的視圖。右鍵單擊該視圖名稱,再選擇“編寫視圖腳本爲”

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章