開篇介紹
記得筆者在 2006年左右剛開始學習 SQL Server 2000 的時候,遇到一個面試題就是行轉列,列轉行的操作,當時寫了很長時間的 SQL 語句最終還是以失敗而告終。后來即使能寫出來,也是磕磕碰碰的,雖然很能鍛煉 SQL 功底,每次都要掙扎一番,溺水的感覺。記得SQL Server 2005 以后就有了 PIVOT 和 UNPIVOT 這兩個函數,可以非常方便的實現行轉列和列傳行的操作,就不再那么掙扎了。后來,在一個 08 項目中,有一位新的女同事在改一個 ETL,發現 SSIS 包中有一個PIVOT 控件不知道怎么用就叫我幫忙。雖然我覺得花點時間還是可以搞定的,但是為了趕回家看一場球賽,找了一個不靠譜的接口就扔下她一個人給跑了。因為項目應該很急,每個人壓力其實都很大,不記得是當天晚上就要交付還是第二天要交付。現在想想,很內疚也非常敗人品,因為平時大家伙還都比較信任我,但是關鍵時刻跑了,確實有點不太負責任。今天正好整理到這一部分的筆記就想到了這個疙瘩,山東的那位妹子如果看到了,說聲對不起吧!
SSIS 筆記整理到這幾個地方,就來總結一下 PIVOT 的使用,如果之前不會用的,看了這篇文章就可以明白了。
測試代碼
IF OBJECT_ID('T040_PRODUCT_SALES') IS NOT NULL
DROP TABLE T040_PRODUCT_SALES
GO
CREATE TABLE T040_PRODUCT_SALES ( ID INT IDENTITY(1,1), ProductName VARCHAR(20), SaleMonth INT, SalesCount INT ) -- Inserting test data INSERT INTO T040_PRODUCT_SALES VALUES ('Bicycle',1,1), ('Shoes',2,2), ('Clothes',3,3), ('Books',4,4), ('Medicine',5,5), ('Drinks',6,6), ('Shoes',7,7), ('Books',1,2), ('Bicycle',1,3), ('Medicine',1,4), ('Clothes',1,5), ('Mobile Phone',1,6), ('Books',1,7), ('Medicine',1,8), ('Shoes',1,9), ('Bicycle',2,10) SELECT ProductName, SaleMonth, SUM(SalesCount) AS SalesCount FROM T040_PRODUCT_SALES GROUP BY ProductName, SaleMonth ORDER BY ProductName, SaleMonth

我們需要實現的效果是按產品名稱,1月,2月,3月,4月,5月,6月 這七個列來顯示 SalesCount 的總數。

怎么來實現這種行列轉換效果,只要把下面這個點就理解清楚,照着寫就可以實現。
/****
SELECT 非透視列,
[透視列 1] AS '列名1',
[透視列 2] AS '列名2',
[透視列 3] AS '列名3'
FROM (
-- 源數據
SELECT 非透視列,
透視列值的來源列,
需要聚合的值
FROM 表
)AS 別名
PIVOT
(
SUM(需要聚合的值)
FOR 透視列值的來源列 IN ([透視列 1],[透視列 2],[透視列 3])
)AS 別名
****/
對照上面的語法,我們弄清楚這些對應關系:
- 非透視列 - 一般是第一列,把效果想出來,ProductName 就是位於第一列,它是非透視列。
- 透視列 - 就是需要由列變為行的那些列,哪些行中的值需要作為列來顯示? 1月 - 6月。
- 透視列值的來源列 - 就是 SaleMonth,這列包含了 1月 - 6月的值。
- 需要聚合的值 - 就是 SalesCount。
把這些需求理解了,就直接按照上面的這個語法就可以實現了,沒有一點點多余的代碼。
SELECT ProductName,
ISNULL([1],0) AS '1', ISNULL([2],0) AS '2', ISNULL([3],0) AS '3', ISNULL([4],0) AS '4', ISNULL([5],0) AS '5', ISNULL([6],0) AS '6' FROM( SELECT ProductName, SaleMonth, SalesCount FROM T040_PRODUCT_SALES )AS Sales PIVOT ( SUM(SalesCount) FOR SaleMonth IN([1],[2],[3],[4],[5],[6]) )AS PIVOTBL

SSIS 中 Pivot 的實現
在數據流中添加一個 OLE DB Source 並配置源測試表。

添加一個 Pivot 控件。

對 Pivot 的配置

Pivot Key - 透視列。透視列中的每一個值(去重之后)將會形成一個新的列。
Set Key - 非透視列,需要和透視列一起顯示的聚合列。
Pivot Value - Pivot Key 和 Set Key 一起關聯的結果值。
之所以要選擇 Ignore un-matched pivot key values and report them after DataFlow execution 是因為:
Pivot 轉換控件是一個靜態的狀態控件,它需要清楚的知道在 Pivot Key 中有哪些確定的值,它需要基於這些值來創建相應的輸出列。勾選中它並執行一次包之后,就可以在 Progress/中看到唯一的 Pivot Key 列表,我們在設計的階段就可以通過這些列表上的值來確定最終我們需要創建的列。

比如,確定了只需要 1月份 - 7月份的作為新的輸出列,將這些只拷貝到指定的位置中,根據這些透視列來創建物理表中的透視列。

下面是創建之后的列的名稱。

這樣整個配置就完成了。

執行的時候查看一下數據,基本上反映出了行轉列的結構變化。這不過這些數據本身上看上去有一些重復,且沒有聚合。

因此我們應該在查詢結果先把需要聚合好的內容聚合好,這樣在 PIVOT 轉換控件中就直接進行行,列轉換而不是轉換在聚合,這樣效率更高一些。

按照 ProductName 和銷售月份實現了對數據的行列轉換,數據信息的易讀性顯而易見。

當然也可以看看 Show Advanced Editor 中的內容。

在這里可以看到 Pivot 轉換控件之前有 3 個數據源列,在經過 Pivot 轉換之后,其 Output Columns 變成 8 列向下輸出。

更多 BI 文章請參看 BI 系列隨筆列表 (SSIS, SSRS, SSAS, MDX, SQL Server) 如果覺得這篇文章看了對您有幫助,請幫助推薦,以方便他人在 BIWORK 博客推薦欄中快速看到這些文章。
