如何在SQL Server 2008/2012中将列值转为行值(生成Pivot格式)
在SQL Server 2008/2012中实现行转列(Pivot)需求
没问题!针对你给出的测试数据和期望的透视结果,我们可以在SQL Server 2008和2012中用两种实用方案实现:静态Pivot(适合日期固定的场景)和动态Pivot(应对日期不固定、可能新增的情况),下面详细说明:
静态Pivot实现(日期固定时用)
如果你的目标日期是确定的(比如例子里的2018-01-15、2018-01-16、2018-01-17),直接用静态语句就能快速得到结果:
-- 先执行你提供的测试表创建和数据插入代码 create table #testpivot ( buyer nvarchar(1000), Amount money, TranscDate datetime ) go insert into #testpivot values ('Sam', 100, '2018-01-15 10:19:34.400'), ('Sam', 200, '2018-01-15 11:19:34.400'), ('Sam', 300, '2018-01-15 21:19:34.400'), ('Ed', 100, '2018-01-15 10:19:34.400'), ('Ed', 100, '2018-01-16 14:19:34.400'), ('Ed', 100, '2018-01-16 08:19:34.400'), ('Ed', 100, '2018-01-17 12:19:34.400'), ('Ed', 100, '2018-01-17 01:19:34.400') go -- 静态Pivot查询语句 SELECT buyer, ISNULL([01/15/2018], 0) AS [01/15/2018], ISNULL([01/16/2018], 0) AS [01/16/2018], ISNULL([01/17/2018], 0) AS [01/17/2018], TotalAmt FROM ( SELECT buyer, -- 把日期转换为MM/DD/YYYY格式的字符串,方便作为Pivot的列 CONVERT(varchar, TranscDate, 101) AS TranscDateStr, Amount, -- 用窗口函数计算每个买家的总金额 SUM(Amount) OVER (PARTITION BY buyer) AS TotalAmt FROM #testpivot ) AS SourceTable PIVOT ( -- 对Amount求和,作为每个日期列的值 SUM(Amount) -- 指定要转成列的日期值 FOR TranscDateStr IN ([01/15/2018], [01/16/2018], [01/17/2018]) ) AS PivotTable ORDER BY buyer; -- 清理临时表 DROP TABLE #testpivot; go
关键点解释:
- 子查询
SourceTable:将原始的datetime类型日期转换为MM/DD/YYYY格式的字符串,同时用SUM() OVER (PARTITION BY buyer)计算每个买家的总金额,避免后续重复计算。 PIVOT运算符:指定聚合函数SUM(Amount),把日期字符串的不同值转成单独的列。ISNULL():将没有数据的日期列值从NULL替换为0,完全匹配你期望的结果样式。
动态Pivot实现(日期不固定时用)
如果你的表中会新增不同的日期,静态Pivot就需要手动修改语句,这时候用动态SQL可以自动获取所有日期并生成对应的列:
-- 同样先创建测试表并插入数据 create table #testpivot ( buyer nvarchar(1000), Amount money, TranscDate datetime ) go insert into #testpivot values ('Sam', 100, '2018-01-15 10:19:34.400'), ('Sam', 200, '2018-01-15 11:19:34.400'), ('Sam', 300, '2018-01-15 21:19:34.400'), ('Ed', 100, '2018-01-15 10:19:34.400'), ('Ed', 100, '2018-01-16 14:19:34.400'), ('Ed', 100, '2018-01-16 08:19:34.400'), ('Ed', 100, '2018-01-17 12:19:34.400'), ('Ed', 100, '2018-01-17 01:19:34.400') go DECLARE @cols AS NVARCHAR(MAX), @query AS NVARCHAR(MAX); -- 自动生成所有日期列的字符串,格式为[MM/DD/YYYY] SELECT @cols = STUFF((SELECT ',' + QUOTENAME(CONVERT(varchar, TranscDate, 101)) FROM #testpivot GROUP BY CONVERT(varchar, TranscDate, 101) ORDER BY CONVERT(datetime, CONVERT(varchar, TranscDate, 101)) FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)') ,1,1,''); -- 构建动态Pivot查询语句 SET @query = 'SELECT buyer, ' + @cols + ', TotalAmt FROM ( SELECT buyer, CONVERT(varchar, TranscDate, 101) AS TranscDateStr, Amount, SUM(Amount) OVER (PARTITION BY buyer) AS TotalAmt FROM #testpivot ) AS SourceTable PIVOT ( SUM(Amount) FOR TranscDateStr IN (' + @cols + ') ) AS PivotTable ORDER BY buyer'; -- 执行动态SQL语句 EXEC sp_executesql @query; -- 清理临时表 DROP TABLE #testpivot; go
关键点解释:
- 生成列列表
@cols:用STUFF和FOR XML PATH把所有不同的日期(转换为MM/DD/YYYY格式)拼接成带方括号的字符串,比如[01/15/2018],[01/16/2018],[01/17/2018],确保所有日期都被包含。 - 动态查询:把生成的列列表插入到Pivot语句中,用
sp_executesql执行,这样不管后续新增什么日期,都会自动作为列显示,不需要手动修改语句。
两种方案都能完美得到你想要的结果,静态方案更直观易读,适合日期固定的场景;动态方案扩展性更强,适合日期不固定的业务场景。
内容的提问来源于stack exchange,提问作者chokka26a
相关产品推荐
相关产品推荐

