You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:55:06