SQL Server PIVOT 详解

PIVOT:行转列(透视) ,把某一列的值,横向变成新的列;必须搭配聚合函数 (SUM/COUNT/AVG/MAX/MIN),SQL Server 2005+支持 UNPIVOT 是反向操作:列转行(逆透视)

一、核心三要素(必记)

  1. 行分组列(不透视列) :最终结果里保留、用来分组的字段(比如姓名、销售员)
  2. 透视列(FOR 后面) :它里面的值,变成新的列名(比如科目、月份)
  3. 聚合值列(聚合函数里面) :要填充到交叉单元格的数据(成绩、金额)

底层等价于 CASE WHEN + GROUP BY,PIVOT 写法更简洁

二、标准语法

vbnet 复制代码
SELECT 不透视列, [值1] AS 别名, [值2] AS 别名, [值3]
FROM (
    -- 内层子查询:只保留【分组列、透视列、值列】三列!多余字段不要带
    SELECT 分组列, 透视列, 值列 FROM 表
) AS 子查询别名
PIVOT (
    聚合函数(值列)
    FOR 透视列 IN ([值1],[值2],[值3]) -- 固定写死,静态PIVOT
) AS Pivot别名
ORDER BY 分组列;

⚠️ 重点坑:内层子查询不要带无关字段,否则会被当成分组字段,导致结果行变多、数据错乱!

三、静态PIVOT示例(最常用)

测试表

sql 复制代码
CREATE TABLE Score(
    Name NVARCHAR(10),
    Subject NVARCHAR(10),
    Score INT
);
INSERT INTO Score VALUES
('张三','语文',80),('张三','数学',90),('张三','英语',75),
('李四','语文',88),('李四','数学',76),('李四','英语',92);

原始长表:

Name Subject Score
张三 语文 80
张三 数学 90
张三 英语 75

目标:转为宽表,一行一个学生,语文、数学、英语作为列

vbnet 复制代码
SELECT 
    Name,
    [语文] AS 语文成绩,
    [数学] AS 数学成绩,
    [英语] AS 英语成绩
FROM (
    SELECT Name, Subject, Score FROM Score
) AS t
PIVOT (
    MAX(Score) -- 同一个人同一科目只有一条,MAX/SUM都可以
    FOR Subject IN ([语文],[数学],[英语])
) AS pvt;

输出:

Name 语文成绩 数学成绩 英语成绩
张三 80 90 75
李四 88 76 92

对比等价CASE写法(方便理解底层)

sql 复制代码
SELECT Name,
MAX(CASE WHEN Subject='语文' THEN Score END) AS 语文,
MAX(CASE WHEN Subject='数学' THEN Score END) AS 数学,
MAX(CASE WHEN Subject='英语' THEN Score END) AS 英语
FROM Score
GROUP BY Name;

四、动态PIVOT(透视值不固定,重点!)

静态PIVOT的 IN([语文],[数学]) 是写死的;如果科目/月份不确定,需要拼接SQL字符串 + sp_executesql

sql 复制代码
DECLARE @cols NVARCHAR(MAX),@sql NVARCHAR(MAX);
-- 自动拼接所有透视列,去重
SELECT @cols = STUFF((SELECT DISTINCT ',' + QUOTENAME(Subject) 
                      FROM Score FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'),1,1,'');

SET @sql = N'
SELECT Name, ' + @cols + '
FROM (SELECT Name,Subject,Score FROM Score) t
PIVOT (MAX(Score) FOR Subject IN (' + @cols + ')) pvt';

EXEC sp_executesql @sql;
  • QUOTENAME():自动加方括号,防止字段名是数字、特殊字符报错
  • STUFF + FOR XML PATH:SQL Server经典拼接列字符串方案

五、UNPIVOT 逆透视(列转行)

把宽表转回长表

css 复制代码
-- 先构造宽表cte
WITH cte AS (
SELECT Name,[语文],[数学],[英语] FROM (SELECT Name, Subject, Score FROM Score) t
PIVOT (MAX(Score) FOR Subject IN ([语文],[数学],[英语])) pvt
)
SELECT Name, Subject, Score
FROM cte
UNPIVOT (
    Score FOR Subject IN ([语文],[数学],[英语])
) AS unpvt;

UNPIVOT 不会还原PIVOT之前完全一模一样的数据:聚合丢失的信息无法恢复,NULL值会直接过滤掉

六、高频坑与注意事项

  1. 必须聚合:PIVOT语法强制要求聚合函数;即使一行一条数据,也要写MAX/MIN
  2. NULL处理 :没有对应数据时,透视单元格默认NULL;如果要显示0,外面套 ISNULL(列,0)
vbnet 复制代码
SELECT Name, ISNULL([语文],0) AS 语文 FROM ...
  1. 内层子查询字段控制:内层只保留3个核心字段,多余字段会参与分组,结果炸裂
  2. 透视值是数字 :一定要包 [],比如 [2025],否则语法报错
  3. 排序:PIVOT里IN的顺序 = 输出列顺序;动态PIVOT需要额外控制排序
  4. 性能:大表慎用动态PIVOT;PIVOT多次嵌套性能差
  5. 兼容级别:数据库兼容级别≥90(2005)才支持PIVOT

七、常用聚合选择参考

  • 每个分组+透视值只有一条:MAX / MIN
  • 求和、汇总:SUM(销售金额)
  • 计数:COUNT(字段)
  • 平均值:AVG

八、PIVOT vs CASE WHEN 怎么选

  • PIVOT:透视列少、写起来短、可读性好;静态固定列简单,动态需要拼接字符串
  • CASE WHEN:灵活,支持多条件、复杂计算;适合复杂业务,不需要动态拼接也能控制NULL默认值
相关推荐
betazhou24 天前
Apache Seatunnel 抽取SQL server实时同步到Oracle(四)
数据库·oracle·apache·sql server·seatunnel
ProcessOn25 天前
纯干货!MySql实战02:一条SQL更新语句执行流程大探索!
sql·mysql·sql server
ClouGence1 个月前
开源数据库管理工具 CloudDM 4.3.0 发布,支持 SQL 收藏,多数据源语法支持大幅增强
数据库·oracle·sql server
zengjuan10052 个月前
恢复实战全过程:从数据库崩溃到业务恢复,我用松鼠备份只花了25分钟
数据恢复·sql server·数据备份·备份策略·数据库备份·松鼠备份·防勒索病毒
Darling噜啦啦2 个月前
Text2SQL 实战:自然语言直连数据库,让 AI 自动写 SQL 完成 CRUD 与多表联查
llm·sql server
学以智用2 个月前
SQL Server 2008 SQL注入详解
sql server
zengjuan10053 个月前
SQL Server 恢复模式避坑指南:简单 / 完整 / 大容量日志选错,日志备份白做
数据恢复·sql server·备份策略·数据库备份·数据库运维·松鼠备份·数据库容灾
倔强的石头1063 个月前
SQL Server数据迁移不只是把数据搬过去
sql server·数据迁移
随手工具-Excel, pdf, SQL3 个月前
没有 SSMS 导入向导?Excel/CSV 一键生成 SQL Server INSERT 脚本
excel·sql server·在线工具·insert·测试数据·ssms·t-sql