CASE 表达式是什么
CASE 是 SQL 里的条件分支表达式,有两种写法:简单 CASE 拿一个表达式逐个与 WHEN 后面的值做等值比较,搜索 CASE 则在 WHEN 后面写任意布尔条件。分支自上而下取第一个命中,后面的分支不再判断;都不命中时返回 ELSE 的值,缺省 ELSE 就返回 NULL。它在报表里很实用:一次扫描就能同时算出多个口径的指标,也常用来把编码翻译成可读名称,或处理 NULL 这类特殊分支。
基本信息
| 简单 CASE | CASE expr WHEN v THEN r … END,用等值比较 |
|---|---|
| 搜索 CASE | CASE WHEN cond THEN r … END,条件任意写 |
| 匹配规则 | 自上而下取首个命中,之后的分支不再判断 |
| ELSE | 缺省时返回 NULL,建议显式写出 |
| 可写位置 | SELECT、WHERE、ORDER BY、GROUP BY 都可用 |
| 适用平台 | 标准 SQL,MySQL 8.0 与 PostgreSQL 一致 |
典型使用场景
- 编码翻译:把 status 数字转成中文名称
- 分箱统计:按金额区间、分数段把记录归类
- 一次算多口径:用几个 CASE 在同一查询里生成不同状态的计数
- 条件排序:ORDER BY CASE WHEN 让指定记录置顶
- 条件更新:UPDATE 时按不同条件写入不同值
- 处理 NULL:用 CASE WHEN col IS NULL 单独分支
两种形式
两种形式
-- 简单 CASE:等值匹配
SELECT name,
CASE level WHEN 1 THEN '低' WHEN 2 THEN '中' ELSE '高' END AS level_name
FROM tasks;
-- 搜索 CASE:任意条件,注意条件自上而下判断
SELECT score,
CASE WHEN score >= 90 THEN 'A'
WHEN score >= 80 THEN 'B'
ELSE 'C' END AS grade
FROM exams;一次算出多个口径
一次算出多个口径
SELECT city,
COUNT(*) AS total_cnt,
SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) AS valid_cnt, # 有效
SUM(CASE WHEN status = 2 THEN 1 ELSE 0 END) AS pending_cnt, # 待审
SUM(CASE WHEN phone IS NULL THEN 1 ELSE 0 END) AS miss_phone # 缺失
FROM users
GROUP BY city;写法与注意
| 写法 | 行为 | 注意 |
|---|---|---|
| 简单 CASE | 等值比较 | 判断 NULL 不会命中任何分支 |
| 搜索 CASE | 任意布尔条件 | 条件要有顺序,先写更严格的 |
| 缺 ELSE | 返回 NULL | 未覆盖的分支会静默变空 |
| CASE 里做聚合 | 需要对每行输出值 | 写成 SUM(CASE WHEN … THEN 1 ELSE 0 END) |
简单 CASE 用等值比较,判断 NULL 时不会命中任何分支,因为 NULL = NULL 得到 UNKNOWN;需要判空要用搜索形式的 CASE WHEN col IS NULL。
忘记写 ELSE 时未覆盖的分支行会返回 NULL,统计报表里常表现为某些分类莫名少数据,写完先跑一遍各分支的行数分布。
条件排序要与业务口径一致:分数段判断必须从高到低写,先写 score >= 60 会把 90 分也归到及格档。
使用要点
- ELSE 尽量显式写出,避免未覆盖分支静默变 NULL
- 条件顺序从严格到宽松,避免被前面宽泛的分支截胡
- 做条件计数时用 SUM(CASE WHEN … THEN 1 ELSE 0 END),比多次查询省资源
- 分组前用 CASE 归类,可以让报表维度保持一致
- 复杂分支写进子查询或 CTE,主查询只做汇总,可读性更好
- 迁移数据库时注意 IF 与 IIF 这类专有函数不能直接搬