SQL行转列与列转行
目录
先准备点数据:
CREATE TABLE Sell
(
[Year] INT,
[Quarter] NVARCHAR(10),
Quantity INT
)
GO
INSERT INTO Sell
SELECT 2006, 'Q1', 20 UNION ALL
SELECT 2006, 'Q2', 15 UNION ALL
SELECT 2006, 'Q2', 4 UNION ALL
SELECT 2006, 'Q3', 12 UNION ALL
SELECT 2006, 'Q4', 18 UNION ALL
SELECT 2007, 'Q1', 10 UNION ALL
SELECT 2007, 'Q2', 10 UNION ALL
SELECT 2008, 'Q1', 8 UNION ALL
SELECT 2008, 'Q2', 7 UNION ALL
SELECT 2008, 'Q3', 5 UNION ALL
SELECT 2008, 'Q3', 10 UNION ALL
SELECT 2008, 'Q4', 9
GO
1. 何为行列互换
简单地说,就是实现如下形式的相互转换
行转列是将某行中的值作为了标题,而列转行就是将标题变为了列中的值。
2. 行转列
1) 静态
静态适合于要转换为行的列值是确定的一些值,或值不是很多的情况
-- MSSQL 2005+ Pivot 静态(行转列)
SELECT *
FROM sell PIVOT( SUM(quantity) FOR [quarter] IN ( Q1, Q2, Q3, Q4 ) ) p
-- MSSQL 2000+ 静态(行转列)
SELECT
[Year],
SUM(CASE [Quarter] WHEN N'Q1' THEN Quantity ELSE 0 END) Q1,
SUM(CASE [Quarter] WHEN N'Q2' THEN Quantity ELSE 0 END) Q2,
SUM(CASE [Quarter] WHEN N'Q3' THEN Quantity ELSE 0 END) Q3,
SUM(CASE [Quarter] WHEN N'Q4' THEN Quantity ELSE 0 END) Q4
FROM Sell
GROUP BY [Year]
2) 动态
当要转换为行的列值不确定或非常多的时候,可以用动态 SQL
-- MSSQL 2005+ Pivot 动态(行转列)
DECLARE @sql VARCHAR(1000), @distinct_val VARCHAR(500) ;
SET @sql = 'SELECT * FROM sell PIVOT(SUM(quantity) FOR [quarter] IN (' ;
SET @distinct_val = (SELECT STUFF(( SELECT DISTINCT ',' + [quarter] FROM sell FOR XML PATH('')), 1, 1, '')) ;
SET @sql = @sql + @distinct_val + '))' + ' p'
EXEC(@sql)
-- MSSQL 2000+ 动态(行转列)
DECLARE @sql VARCHAR(2000)
SET @sql = 'SELECT [Year]'
SELECT @sql = @sql + ',SUM(CASE [Quarter] WHEN ''' + [Quarter] + ''' THEN [Quantity] ELSE 0 END) ' + [Quarter]
FROM sell
GROUP BY [Quarter]
SET @sql = @sql + ' FROM Sell GROUP BY [Year]'
EXEC(@sql)
3. 列转行
列转行用动态比较麻烦,而且不好把控新的标题,其实实现的思路和上面行转列一样,就是找规律构造SQL。这里仅给出静态(假设上面语句的结果已保存在表 Sell_2 中):
-- 用 unpovit
select [Year],[Quarter],[Quantity]
from sell_2 unpivot(Quantity for [Quarter] in(Q1,Q2,Q3,Q4)) b
-- 不用 unpivot(静态)
select [Year],'Q1' [Quarter], Q1 [Quantity] from Sell_2 union all
select [Year],'Q2' [Quarter], Q2 [Quantity] from Sell_2 union all
select [Year],'Q3' [Quarter], Q3 [Quantity] from Sell_2 union all
select [Year],'Q4' [Quarter], Q4 [Quantity] from Sell_2
order by 1,2
4. 小结
1) Pivot 大致的执行过程:先按未出现在Pivot表达时中的列进行分组,再按in中的枚举项分别聚合。其实和select case when 类似。
2) 当列值不确认时,可以构造动态SQL实现;
3) 列转行Pivot和select .. Case…的性能差不多,但行转列,用unPivot 相比union [all]性能优势非常明显,毕竟后者要多次访问表,而且unPivot还更简洁。
4) Pivot 和 unPiovt 并非完全可逆,因为Pivot的时候,行已经被聚合过了。