SQL 行列转换怎么做?PIVOT、条件聚合和 UNPIVOT 如何选择?
简化版
SQL 行列转换分为行转列和列转行。行转列常用条件聚合,例如按月份把销售额转成 jan_amount、feb_amount;部分数据库支持 PIVOT 语法。列转行可以用 UNION ALL 或数据库提供的 UNPIVOT。选择时要看数据库兼容性、列是否固定、是否需要动态 SQL。
详细版
行列转换本质是改变结果集的展示结构。行转列适合报表展示,让多行分类值变成多列指标;列转行适合数据清洗,把宽表变成长表,方便统一聚合和分析。
如果目标列固定,条件聚合最通用。如果目标列来自动态数据,比如动态月份、动态类目,纯静态 SQL 很难生成未知列,通常需要应用层拼 SQL、存储过程或交给 BI 工具。
面试中要强调:SQL 查询结果的列结构通常需要在执行前确定,动态列不是普通 GROUP BY 能直接解决的。
完整版教学
一、行转列的典型场景
原始订单表按月份存多行。
报表希望一个用户一行,每个月销售额一列。
这就是行转列。
行转列关注的是把分类值变成列名。
常见分类包括月份、状态、渠道、等级。
二、条件聚合实现行转列
SELECT
user_id,
SUM(CASE WHEN month = '2026-01' THEN amount ELSE 0 END) AS amount_202601,
SUM(CASE WHEN month = '2026-02' THEN amount ELSE 0 END) AS amount_202602,
SUM(CASE WHEN month = '2026-03' THEN amount ELSE 0 END) AS amount_202603
FROM sales
GROUP BY user_id;
这种写法兼容性好,适合列集合已知的报表。
三、PIVOT 的特点
| 方案 | 优点 | 缺点 |
|---|---|---|
| 条件聚合 | 通用、可控、兼容性好 | 列多时代码较长 |
| PIVOT | 语义直观 | 数据库差异明显 |
| 动态 SQL | 能处理动态列 | 复杂且要防注入 |
| 应用层转换 | 灵活 | 数据量大时成本高 |
四、列转行的典型场景
宽表里有 score_math、score_english、score_physics。
分析系统希望变成 subject 和 score 两列。
这就是列转行。
列转行之后更适合统一筛选、聚合和可视化。
五、UNION ALL 实现列转行
SELECT student_id, 'math' AS subject, score_math AS score FROM scores
UNION ALL
SELECT student_id, 'english' AS subject, score_english AS score FROM scores
UNION ALL
SELECT student_id, 'physics' AS subject, score_physics AS score FROM scores;
如果数据库支持 UNPIVOT,也可以使用专用语法,但跨库兼容性要评估。
六、动态列为什么麻烦
SQL 结果集的列名通常在解析和规划阶段就要确定。
如果月份来自数据表,数据库不会自动把未知数量的月份变成未知数量的列。
动态列需要动态生成 SQL,或者在应用层、BI 层处理。
行列转换的难点不是聚合本身,而是“列集合是否固定”。
七、误区和追问
- 误区:GROUP BY 可以自动把行变成列。 GROUP BY 只产生分组行,不会自动生成动态列。
- 误区:PIVOT 是标准通用语法。 不同数据库支持程度和写法差异较大。
- 误区:动态 SQL 只是字符串拼接。 动态 SQL 要处理权限、注入、执行计划和维护成本。
- 追问:列很多时条件聚合怎么维护? 可以模板生成 SQL,或交给报表层处理。
- 追问:行列转换会影响性能吗? 会,尤其是大表聚合和动态转换,要关注过滤条件和预聚合。
- 追问:为什么列转行常用 UNION ALL? 因为它保留所有行,不做去重,比 UNION 更符合数据展开语义。
八、面试回答重点
先判断行转列还是列转行。
再说明列集合固定用条件聚合或 PIVOT,动态列用动态 SQL 或应用层。
最后补充兼容性和性能风险。