订阅更新

SQL PIVOT 和 UNPIVOT 有什么区别?

面试问 078 约 2 分钟读完

本文目录
  1. 面试怎么回答
  2. PIVOT:从长表变宽表
  3. UNPIVOT:从宽表变长表
  4. UNPIVOT 不一定能还原 PIVOT 前的数据
  5. 动态列是生产中的真正难点

面试怎么回答

PIVOT 把行中的分类值展开为列,并对同一输出单元格执行聚合;UNPIVOT 把多个列转成“属性名 + 属性值”的多行。二者不是严格可逆,因为 PIVOT 聚合后会丢失明细。实战中我会把稳定长表作为公共存储,只在展示层做有限列的 PIVOT;还要检查动态列导致的 Schema 变化,以及不同引擎对 NULL 和语法的差异。

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

同一份月度销售数据,报表希望“月份变成列”,下游算法却要求“每个月一行”。表里的值没变,数据形状的要求完全相反。

这就是 PIVOT 和 UNPIVOT 要解决的问题:PIVOT 把某列的取值展开为多列,并配合聚合;UNPIVOT 把多列收拢为属性和值两列,转成多行。

PIVOT:从长表变宽表

原始表是“一行一个商品、月份、销售额”:

product_id | month | amount
A          | 01    | 100
A          | 02    | 120

PIVOT 后可以变成:

product_id | m01 | m02
A          | 100 | 120

以 Spark SQL 为例:

SELECT *
FROM sales
PIVOT (
  SUM(amount)
  FOR month IN ('01' AS m01, '02' AS m02)
);

这里不只是把值搬到列上,还发生了聚合。如果同一商品同一月份原本有多行,会先由 SUM(amount) 汇总。

UNPIVOT:从宽表变长表

SELECT *
FROM monthly_sales
UNPIVOT (
  amount FOR month IN (m01 AS '01', m02 AS '02')
);

结果重新变为“月份一行”。这种形态更适合统一计算、时间序列分析和追加新记录。

宽表与长表转换

UNPIVOT 不一定能还原 PIVOT 前的数据

这是最容易答漏的一点。PIVOT 可能把多行聚合成一个值,明细已经丢失;再 UNPIVOT 只能恢复为长表形状,无法把聚合前的多条明细重新拆出来。

另外,NULL 的处理也要看数据库语义。以 Spark SQL 为例,UNPIVOT 默认排除值为 NULL 的行,可以显式使用 INCLUDE NULLS 保留。跨引擎迁移时要检查支持的语法和默认行为。

动态列是生产中的真正难点

如果月份固定,提前列出 12 个值很简单;如果要按不断新增的渠道、商品类别做动态 PIVOT,输出 Schema 会持续变化。下游表结构、BI 字段和数据接口都可能跟着失稳。

因此明细层和公共汇总层通常更适合保存稳定的长表结构。PIVOT 更常放在面向展示的查询层,并限制为明确、有限的列集合。

PIVOT 使用边界

当数据库不支持 PIVOT,或者希望兼容更多引擎,也可以用条件聚合:

SUM(CASE WHEN month = '01' THEN amount END) AS m01

它更通用,但列很多时 SQL 会变长。无论使用哪种方式,都要先确认输入粒度、聚合函数和列值范围。

刷题进度保存在当前浏览器