1. 为什么 CASE WHEN 值得你专门花时间搞透
写 SQL 写了几年之后,你会慢慢发现一个规律:真正卡住你的往往不是那些复杂到离谱的关联查询,而是看起来特别基础、却暗藏玄机的小表达式。CASE WHEN 就是其中最典型的一个。它出现频率极高——报表取数、数据清洗、字段重分类、行转列、权限控制,几乎每个数据岗位的日常都会跟它打交道。但我也见过不少写了三五年 SQL 的人,对它的理解停留在“二选一”的层面,遇到嵌套条件、聚合场景或者 NULL 陷阱就抓瞎。
这个表达式本质上解决的是“对数据进行条件映射”的问题。你手头有一堆明细数据,想按照业务规则给每一条记录打上一个标签、归入一个分组、或者做一次数值换算,这就是 CASE WHEN 的用武之地。它不像 JOIN 那样负责把表连起来,也不像 GROUP BY 那样负责汇总整组数据,它的核心身份是“逐行计算的条件表达式”——你可以把它放在 SELECT 列表里作为输出字段,也可以放在 WHERE 子句里作为过滤条件,还可以放在 ORDER BY 里做自定义排序,甚至可以嵌进聚合函数里做条件计数。
我个人的建议是,不要把它当成一个“知识点”来学,而是当成一个“思维工具”来用。当你形成了“任何条件映射都可以用 CASE WHEN 表达”的思维习惯,很多复杂的 SQL 场景会瞬间变得清晰。这篇文章我会从语法细节讲到实际案例,再把我踩过的坑和总结的优化经验一并分享出来,希望能帮你彻底吃透这个表达式。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 两种写法:简单函数与搜索函数,用错场景会出大事
CASE WHEN 在 SQL 标准里其实有两种形态,几乎所有主流数据库(MySQL、PostgreSQL、SQL Server、Oracle、SQLite)都支持。它们各有适用场景,不能随便混用。
2.1 简单函数写法
第一种叫简单函数(simple case),语法是这样的:
sql复制CASE column_name
WHEN value1 THEN result1
WHEN value2 THEN result2
ELSE resultN
END
它做的事情很直白:拿 column_name 的值去和每个 WHEN 后面的 value 做等值比较,匹配上了就返回对应的 THEN 结果,全部匹配不上就返回 ELSE 里的值。
举个例子,假设订单表 orders 里有一个状态字段 status,1 代表待支付、2 代表已支付、3 代表已发货、4 代表已完成,你现在要把它转成业务人员能看懂的中文描述:
sql复制SELECT
order_id,
CASE status
WHEN 1 THEN '待支付'
WHEN 2 THEN '已支付'
WHEN 3 THEN '已发货'
WHEN 4 THEN '已完成'
ELSE '未知状态'
END AS status_name
FROM orders;
这种写法的优点是结构紧凑、阅读性好,一眼就能看出是“同一个字段的离散值映射”。但它有一个硬性限制:只能做等值比较。你没法在简单函数里直接写 WHEN status > 2 THEN ... 这种范围判断,一旦出现这种需求,就得切换到第二种写法。
2.2 搜索函数写法
第二种叫搜索函数(searched case),语法是这样的:
sql复制CASE
WHEN condition1 THEN result1
WHEN condition2 THEN result2
ELSE resultN
END
注意,CASE 关键字后面不跟表达式,WHEN 后面直接跟完整的条件判断,每个条件都可以是独立的逻辑表达式,支持 =、>、<、>=、<=、<>、LIKE、IN、BETWEEN、IS NULL 等任意运算,多个条件还可以用 AND、OR 组合。
继续用订单表来举例,现在要根据订单金额做等级划分:金额大于 5000 的算高价值订单,1000 到 5000 的算中价值订单,小于 1000 的算普通订单,另外还需要把金额为空的订单单独标记出来:
sql复制SELECT
order_id,
amount,
CASE
WHEN amount > 5000 THEN '高价值'
WHEN amount >= 1000 THEN '中价值'
WHEN amount IS NULL THEN '金额缺失'
ELSE '普通'
END AS order_grade
FROM orders;
这种写法是日常开发中使用频率最高的,自由度高,组合能力强,几乎所有复杂的条件映射都是用它实现的。
2.3 关键区别与易错点
这两种写法背后有一个特别容易被忽略的语义差异:搜索函数是“自上而下逐一判断条件,一旦命中就立即返回,后面不再执行”的。这意味着你把条件顺序写反了,结果就会出错。
我举个例子你就明白了。上面那个等级划分,如果有两个条件:
sql复制WHEN amount > 1000 THEN '中价值'
WHEN amount > 5000 THEN '高价值'
假如某条记录的金额是 8000,它先命中了 amount > 1000,直接返回“中价值”,后面的 amount > 5000 永远没机会执行。结果是高价值订单被错分到中价值里。这类错误在代码 review 时非常难发现,因为语法完全正确,数据量少时也不太容易暴露,一旦数据量大了,报表里的分类就会莫名其妙出错。
另外还有一个需要特别注意的地方:CASE WHEN 的匹配逻辑对 NULL 的处理。在简单函数写法下,CASE column WHEN NULL THEN ... 是永远匹配不上的,因为 SQL 里的 NULL 不等于任何值,包括 NULL 本身。你必须写 WHEN column IS NULL,或者改用搜索函数里的 CASE WHEN column IS NULL THEN ...。这是我觉得初学者最容易踩的坑,没有之一。
简单总结一下两种写法的差异,我用一张表帮你理清思路:
| 对比维度 | 简单函数 | 搜索函数 |
|---|---|---|
| 语法形式 | CASE 字段 WHEN 值 THEN 结果 | CASE WHEN 条件 THEN 结果 |
| 支持的操作 | 仅等值比较 | 任意逻辑表达式 |
| NULL 处理 | 不可直接判断,需配合 IS NULL | 可直接用 IS NULL |
| 适用场景 | 单字段离散值映射 | 范围判断、多条件组合、复杂规则 |
| 判断顺序 | 按书写顺序,命中即返回 | 按书写顺序,命中即返回 |
看到没有,两种写法在“顺序敏感性”上是一致的,都是命中即返回。这一点后面我还会在踩坑章节里详细展开。
3. 实战案例:行转列、分段统计、报表字段扩展是怎么用 CASE WHEN 起飞的
理解了语法还不够,真正体现 CASE WHEN 价值的是它在实战场景中的灵活运用。我挑了几个出镜率最高的场景,给你拆解一下具体的写法思路。
3.1 “行转列”的通用套路
行转列(长表转宽表)是数据处理中一块难啃的骨头,但 CASE WHEN 配合聚合函数可以轻松搞定。先看一个经典的场景。
假设有一张学生成绩表 student_scores,字段包括 student_id(学号)、subject(科目)、score(分数),数据长这样:
| student_id | subject | score |
|---|---|---|
| 1001 | 语文 | 88 |
| 1001 | 数学 | 92 |
| 1001 | 英语 | 85 |
| 1002 | 语文 | 76 |
| 1002 | 数学 | 81 |
| 1002 | 英语 | 79 |
现在想把它转成“每个学生一行,语文、数学、英语各占一列”的宽表,SQL 可以这样写:
sql复制SELECT
student_id,
MAX(CASE WHEN subject = '语文' THEN score END) AS chinese_score,
MAX(CASE WHEN subject = '数学' THEN score END) AS math_score,
MAX(CASE WHEN subject = '英语' THEN score END) AS english_sco
