關於UNPIVOT 操作符


 

UNPIVOT 操作符說明

簡而言之,UNPIVOT操作符就是取得一個行的數據集合,然后把每一行都轉換成多個行數據。為了更好地理解,請看下圖:

UNPIVOT Operation

圖1

從上圖中,你能發現UNPOVOT操作符,取得了兩行數據,每行包含三個Price值,然后將這些轉化成6行數據,其中每個產品價格都是一個不同的行。

UNPIVOT 命令制定了兩個不同的列類型。第一個類型是列中不被轉換的。在例子中,ID、產品名字列是這樣的列類型。第二種列類型就是那些被轉換的。諸如ProductCode, Wholesale 和Retail 這三列。在我上面的例子中,那些沒有被轉換的列將被在每套列值集合中重復,而另外的那些列將被轉換成行。

UNPIVOT 語法

下面就是 UNPIVOT 的語法:

SELECT [columns not unpivoted],
	 [unpivot_column],
       [value_column],
FROM
(<source query>)
AS <alias for the source data>
UNPIVOT ( [value_column] FOR [unpivot_column] IN ( <column_list> ) ) 
   AS <alias for unpivot>

Where:

    • [columns not unpivoted]: 不被轉換的列的名字清單。
    • [unpivot_column]: 不轉換的列的名稱。
    • [value_column]: 確定一個列名稱來代表不轉換的列的數據。
    • <source query>: 源數據。
  •       <alias for the source data>: 為源數據轉換后的表確定一個別名。
  •       <column_list>:  被轉換的列的列名稱。
  •      <alias for unpivot>: 為轉換操作的整套生產,確定一個別名。

為了更好地理解我們看下面的例子:

簡單的例子

USE tempdb;
GO
IF object_id('PhoneNumbers') IS NOT NULL DROP TABLE PhoneNumbers;
GO
CREATE TABLE PhoneNumbers (
	PersonID int, 
	HomePhone varchar(12),
	CellPhone varchar(12), 
	Workphone varchar(12), 
	FaxNumber varchar(12));

INSERT INTO PhoneNumbers VALUES 
	(1,Null,'444-555-2931',Null,Null),
	(2,'444-555-1950','444-555-2931',Null, Null),
	(3,'444-555-1950', Null,'444-555-1324','444-555-2310'),
	(4,'444-555-1950','444-555-2931','444-555-1324',
        '444-555-1987');

Listing 1: 創建並填充PhoneNumbers 數據

SELECT PersonID, PhoneType, PhoneNumber
FROM (
	SELECT PersonID, HomePhone, CellPhone, Workphone, FaxNumber
	FROM PhoneNumbers ) AS Src
	UNPIVOT (
		PhoneNumber FOR PhoneType IN 
		(HomePhone, CellPhone, WorkPhone, FaxNumber)) AS UNPVT;

Listing 2: 行列轉換語法例子

執行上面代碼后顯示如下圖:

unpivot

通過這個例子,我們發現執行結果中每行數據只包含一個單一的電話號碼,同時注意到結果中在原表中有幾個號碼不為null則有幾行數據,ID也就有幾次。接下來我們進一步通過使用UNPIVOT來加深認識。

使用兩個UNPIVOT操作符

第二個例子中,我將使用兩個操作符來行轉列來轉換一套名字/值 的兩列數據。具體如下:

unpivot1

 

在表 CustPref里面 我有四對名稱和值。

我們將使用不同的UNPIVOT操作符來創建一個結果集,每一個PrefType的名字和值針對每個CustID 和CustName。並聯使用操作符的作用是為了轉換兩組列。這樣講能表示為一個參數名稱和值在一行里面。執行代碼如下:

3http://www.cnblogs.com/wenBlog/

通過這個輸出結果,能發現不同的type對應不同的值得列,並且要關聯CustID。整個查詢通過兩個不同的UNPOVOT操作符同時使用了where 子句來合並輸出結果(基於列名前五個字符相同的進行匹配),第一個行轉列轉換的是數據,第二個為類型,where限制了比較前五個字符,我能取得匹配的數據組。

動態UNPIVOT查詢

代碼如下:

USE tempdb;
GO
DECLARE @ColNames varchar(1000);
SET @ColNames = '';
-- Get PrefValue Columns
SELECT @ColNames=stuff((
    SELECT DISTINCT ',' + QUOTENAME(COLUMN_NAME)
    FROM INFORMATION_SCHEMA.COLUMNS p2
    WHERE TABLE_NAME = 'CustPref'
	  AND COLUMN_NAME like 'Pref_Type'
    FOR XML PATH(''), TYPE).value('.', 'varchar(max)')
            ,1,1,'')
-- Get PrefType Columns
DECLARE @ColValues varchar(1000);
SET @ColValues = '';
SELECT @ColValues=stuff((
    SELECT DISTINCT ',' + QUOTENAME(COLUMN_NAME)
    FROM INFORMATION_SCHEMA.COLUMNS p2
    WHERE TABLE_NAME = 'CustPref'
	  AND COLUMN_NAME like 'Pref_Data'
    FOR XML PATH(''), TYPE).value('.', 'varchar(max)')
            ,1,1,'')
-- Generate UNPIVOT Statement
DECLARE @CMD nvarchar(2000);
SET @CMD = 'SELECT CustId, CustName, PrefType, PrefValue FROM ' + 
           '(SELECT CustID, CustName, ' + @ColNames + ',' + @ColValues + 
		   ' FROM CustPref) AS Perf UNPIVOT (PrefValue FOR PrefValues IN (' +  
		   @ColValues + ')) AS UP1 UNPIVOT (PrefType FOR PrefTypes IN (' + 
		   @ColNames + ')) AS UP2 WHERE ' + 
		   'substring(PrefValues,5,1) = substring(PrefTypes,5,1);'
-- Print UNPIVOT Command
PRINT @CMD
-- Execute UNPIVOT Command
execute sp_executesql @CMD

結果是與上面的例子完全相同的。

為了完成和這個動態的SQL,我使用了INFORMATION_SCHEMA.COLUMNS視圖。這個視圖能幫我們設定兩個變量@ColNames和@ColValues ,這就包含了用逗號區分的列名的字符串。這兩個變量被用來構建動態的行轉列查詢。一旦我建立了動態的SQL就能,執行這個sp_executesql了。

這是一個簡單的實例,但是相同的邏輯可以應用於更多的不同的組列的轉換。

Summary

UNPIVOT操作符在2005 首次被引入,允許我們將多個name/value 列從不規范的表中創建到一個規范畫的結果集中,並且一一對應於選定的列。通過使用這個操作符,我們能同時轉換多個不同組的name/value 的成對的列。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM