SQL Server 動態行轉列(參數化表名、分組列、行轉列欄位、欄位值) ...
一.本文所涉及的內容(Contents)
本文所涉及的內容(Contents)
背景(Contexts)
實現代碼(SQL Codes)
方法一:使用拼接SQL,靜態列欄位;
方法二:使用拼接SQL,動態列欄位;
方法三:使用PIVOT關係運算符,靜態列欄位;
方法四:使用PIVOT關係運算符,動態列欄位;
擴展閱讀一:參數化表名、分組列、行轉列欄位、欄位值;
擴展閱讀二:在前面的基礎上加入條件過濾;
參考文獻(References)
二.背景(Contexts)
其實行轉列並不是一個什麼新鮮的話題了,甚至已經被大家說到爛了,網上的很多例子多多少少都有些問題,所以我希望能讓大家快速的看到執行的效果,所以在動態列的基礎上再把表、分組欄位、行轉列欄位、值這四個行轉列固定需要的值變成真正意義的參數化,大家只需要根據自己的環境,設置參數值,馬上就能看到效果了(可以直接跳轉至:“參數化動態PIVOT行轉列”查看具體的腳本代碼)。行轉列的效果圖如圖1所示:
(圖1:行轉列效果圖)
三.實現代碼(SQL Codes)
(一) 首先我們先創建一個測試表,往裡面插入測試數據,返回表記錄如圖2所示:
1 --創建測試表 2 IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TestRows2Columns]') AND type in (N'U')) 3 DROP TABLE [dbo].[TestRows2Columns] 4 GO 5 CREATE TABLE [dbo].[TestRows2Columns]( 6 [Id] [int] IDENTITY(1,1) NOT NULL, 7 [UserName] [nvarchar](50) NULL, 8 [Subject] [nvarchar](50) NULL, 9 [Source] [numeric](18, 0) NULL 10 ) ON [PRIMARY] 11 GO 12 13 --插入測試數據 14 INSERT INTO [TestRows2Columns] ([UserName],[Subject],[Source]) 15 SELECT N'張三',N'語文',60 UNION ALL 16 SELECT N'李四',N'數學',70 UNION ALL 17 SELECT N'王五',N'英語',80 UNION ALL 18 SELECT N'王五',N'數學',75 UNION ALL 19 SELECT N'王五',N'語文',57 UNION ALL 20 SELECT N'李四',N'語文',80 UNION ALL 21 SELECT N'張三',N'英語',100 22 GO 23 24 SELECT * FROM [TestRows2Columns]
(圖2:樣本數據)
(二) 先以靜態的方式實現行轉列,效果如圖3所示:
1 --1:靜態拼接行轉列 2 SELECT [UserName], 3 SUM(CASE [Subject] WHEN '數學' THEN [Source] ELSE 0 END) AS '[數學]', 4 SUM(CASE [Subject] WHEN '英語' THEN [Source] ELSE 0 END) AS '[英語]', 5 SUM(CASE [Subject] WHEN '語文' THEN [Source] ELSE 0 END) AS '[語文]' 6 FROM [TestRows2Columns] 7 GROUP BY [UserName] 8 GO
(圖3:樣本數據)
(三) 接著以動態的方式實現行轉列,這是使用拼接SQL的方式實現的,所以它適用於SQL Server 2000以上的資料庫版本,執行腳本返回的結果如圖2所示;
1 --2:動態拼接行轉列 2 DECLARE @sql VARCHAR(8000) 3 SET @sql = 'SELECT [UserName],' 4 SELECT @sql = @sql + 'SUM(CASE [Subject] WHEN '''+[Subject]+''' THEN [Source] ELSE 0 END) AS '''+QUOTENAME([Subject])+''',' 5 FROM (SELECT DISTINCT [Subject] FROM [TestRows2Columns]) AS a 6 SELECT @sql = LEFT(@sql,LEN(@sql)-1) + ' FROM [TestRows2Columns] GROUP BY [UserName]' 7 PRINT(@sql) 8 EXEC(@sql) 9 GO
(四) 在SQL Server 2005之後有了一個專門的PIVOT 和 UNPIVOT 關係運算符做行列之間的轉換,下麵是靜態的方式實現的,實現效果如圖4所示:
1 --3:靜態PIVOT行轉列 2 SELECT * 3 FROM ( SELECT [UserName] , 4 [Subject] , 5 [Source] 6 FROM [TestRows2Columns] 7 ) p PIVOT 8 ( SUM([Source]) FOR [Subject] IN ( [數學],[英語],[語文] ) ) AS pvt 9 ORDER BY pvt.[UserName]; 10 GO
(圖4)
(五) 把上面靜態的SQL基礎上進行修改,這樣就不用理會記錄裡面存儲了什麼,需要轉成什麼列名的問題了,腳本如下,效果如圖4所示:
1 --4:動態PIVOT行轉列 2 DECLARE @sql_str VARCHAR(8000) 3 DECLARE @sql_col VARCHAR(8000) 4 SELECT @sql_col = ISNULL(@sql_col + ',','') + QUOTENAME([Subject]) FROM [TestRows2Columns] GROUP BY [Subject] 5 SET @sql_str = ' 6 SELECT * FROM ( 7 SELECT [UserName],[Subject],[Source] FROM [TestRows2Columns]) p PIVOT 8 (SUM([Source]) FOR [Subject] IN ( '+ @sql_col +') ) AS pvt 9 ORDER BY pvt.[UserName]' 10 PRINT (@sql_str) 11 EXEC (@sql_str)
(六) 也許很多人到了上面一步就夠了,但是你會發現,當別人拿到你的代碼,需要不斷的修改成他自己環境中表名、分組列、行轉列欄位、欄位值這幾個參數,邏輯如圖5所示,
1 --5:參數化動態PIVOT行轉列 2 -- ============================================= 3 -- Author: <聽風吹雨> 4 -- Create date: <2014.05.26> 5 -- Description: <參數化動態PIVOT行轉列> 6 -- Blog: <http://www.cnblogs.com/gaizai/> 7 -- ============================================= 8 DECLARE @sql_str NVARCHAR(MAX) 9 DECLARE @sql_col NVARCHAR(MAX) 10 DECLARE @tableName SYSNAME --行轉列表 11 DECLARE @groupColumn SYSNAME --分組欄位 12 DECLARE @row2column SYSNAME --行變列的欄位 13 DECLARE @row2columnValue SYSNAME --行變列值的欄位 14 SET @tableName = 'TestRows2Columns' 15 SET @groupColumn = 'UserName' 16 SET @row2column = 'Subject' 17 SET @row2columnValue = 'Source' 18 19 --從行數據中獲取可能存在的列 20 SET @sql_str = N' 21 SELECT @sql_col_out = ISNULL(@sql_col_out + '','','''') + QUOTENAME(['+@row2column+']) 22 FROM ['+@tableName+'] GROUP BY ['+@row2column+']' 23 --PRINT @sql_str 24 EXEC sp_executesql @sql_str,N'@sql_col_out NVARCHAR(MAX) OUTPUT',@sql_col_out=@sql_col OUTPUT 25 --PRINT @sql_col 26 27 SET @sql_str = N' 28 SELECT * FROM ( 29 SELECT ['+@groupColumn+'],['+@row2column+'],['+@row2columnValue+'] FROM ['+@tableName+']) p PIVOT 30 (SUM(['+@row2columnValue+']) FOR ['+@row2column+'] IN ( '+ @sql_col +') ) AS pvt 31 ORDER BY pvt.['+@groupColumn+']' 32 --PRINT (@sql_str) 33 EXEC (@sql_str)
(圖5)
所以,我繼續對上面的腳本進行修改,你只要設置自己的參數就可以實現行轉列了,效果如圖4所示:
(七) 在實際的運用中,我經常遇到需要對基礎表的數據進行篩選後再進行行轉列,那麼下麵的腳本將滿足你這個需求,效果如圖6所示:
1 --6:帶條件查詢的參數化動態PIVOT行轉列 2 -- ============================================= 3 -- Author: <聽風吹雨> 4 -- Create date: <2014.05.26> 5 -- Description: <參數化動態PIVOT行轉列,帶條件查詢的參數化動態PIVOT行轉列> 6 -- Blog: <http://www.cnblogs.com/gaizai/> 7 -- ============================================= 8 DECLARE @sql_str NVARCHAR(MAX) 9 DECLARE @sql_col NVARCHAR(MAX) 10 DECLARE @sql_where NVARCHAR(MAX) 11 DECLARE @tableName SYSNAME --行轉列表 12 DECLARE @groupColumn SYSNAME --分組欄位 13 DECLARE @row2column SYSNAME --行變列的欄位 14 DECLARE @row2columnValue SYSNAME --行變列值的欄位 15 SET @tableName = 'TestRows2Columns' 16 SET @groupColumn = 'UserName' 17 SET @row2column = 'Subject' 18 SET @row2columnValue = 'Source' 19 SET @sql_where = 'WHERE UserName = ''王五''' 20 21 --從行數據中獲取可能存在的列 22 SET @sql_str = N' 23 SELECT @sql_col_out = ISNULL(@sql_col_out + '','','''') + QUOTENAME(['+@row2column+']) 24 FROM ['+@tableName+'] '+@sql_where+' GROUP BY ['+@row2column+']' 25 --PRINT @sql_str 26 EXEC sp_executesql @sql_str,N'@sql_col_out NVARCHAR(MAX) OUTPUT',@sql_col_out=@sql_col OUTPUT 27 --PRINT @sql_col 28 29 SET @sql_str = N' 30 SELECT * FROM ( 31 SELECT ['+@groupColumn+'],['+@row2column+'],['+@row2columnValue+'] FROM ['+@tableName+']'+@sql_where+') p PIVOT 32 (SUM(['+@row2columnValue+']) FOR ['+@row2column+'] IN ( '+ @sql_col +') ) AS pvt 33 ORDER BY pvt.['+@groupColumn+']' 34 --PRINT (@sql_str) 35 EXEC (@sql_str)
(圖6)
四.參考文獻(References)
使用 PIVOT 和 UNPIVOT
註:本文轉載自 聽風吹雨 網址:http://www.cnblogs.com/gaizai/p/3753296.html#_labelFive