case表达式在sql中用于值映射最直接高效,推荐使用搜索型case显式处理null,避免where中嵌套导致索引失效,聚合统计时优先用count(case when...),慎用嵌套及跨库函数。

CASE 表达式在 SELECT 中做值映射最直接
想把数据库里某个字段的原始值(比如状态码 status = 0/1/2)转成可读文字(“待处理”“已完成”“已取消”),CASE 是最轻量、最可控的方式。它不依赖外部字典表,也不需要 JOIN,查一次就出结果。
常见错误是写成 CASE status WHEN 1 THEN '完成' ELSE '未知' END 却忘了 status 字段可能为 NULL —— 这种写法里 NULL 会掉进 ELSE 分支,但你未必想把它和真实值 0 或 2 归为一类。
- 优先用搜索型
CASE WHEN status = 1 THEN ...,能显式覆盖NULL判断(比如加一行WHEN status IS NULL THEN '空值') -
ELSE不要省略,哪怕只是ELSE 'N/A';没写ELSE时,不匹配的行会返回NULL,容易引发前端空指针或报表漏数 - 字符串值记得加单引号,数字不用;混用会导致隐式转换,MySQL 可能不报错但结果异常,PostgreSQL 直接报错
ERROR: column "xxx" is of type integer but expression is of type text
WHERE 里用 CASE 做条件映射很危险
有人想“按中文状态筛选”,写出 WHERE CASE status WHEN 1 THEN '完成' END = '完成' —— 这语法合法,但几乎一定走不了索引,全表扫描风险极高。
真正该做的是反向映射:把查询条件转回原始值。比如前端传 “已完成”,后端应解析为 status = 1,再拼进 WHERE 子句。
- WHERE 中嵌套
CASE通常意味着设计倒置;原始字段有索引,计算列没有 - 如果真要动态多字段映射(如 status 和 type 组合判断),宁可用
OR拆开:WHERE (status = 1 AND type = 'A') OR (status = 2 AND type = 'B') - 某些 ORM(如 MyBatis)支持
<choose></choose>动态生成 SQL,比在 SQL 里硬写CASE条件更安全
聚合统计时 CASE 配合 COUNT/SUM 最常用
要统计“各状态订单数”,不用写三个 SELECT COUNT(*) FROM ... WHERE status = 1,一个 COUNT(CASE WHEN status = 1 THEN 1 END) 全搞定。
注意 COUNT 和 SUM 对 NULL 的处理差异:前者跳过 NULL,后者把 NULL 当 0;所以 COUNT(CASE WHEN status = 1 THEN 1 END) 等价于 SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END),但前者少写 ELSE 更简洁。
- 别写
COUNT(CASE WHEN status = 1 THEN 'x' END)—— 字符串非空也能被计,但语义不清,且可能触发字符集隐式转换 - 涉及金额汇总时,用
SUM(CASE WHEN paid = 1 THEN amount ELSE 0 END),确保未支付订单不参与累加 - MySQL 8.0+ 支持
COUNT_IF(status = 1)这类简写函数,但兼容性差,跨库迁移容易翻车
嵌套 CASE 容易写错括号和逻辑优先级
当映射规则有层级(比如先看 type,再根据 status 细分),嵌套 CASE 很快变得难读且易错。
典型坑是括号闭合位置不对,或者漏了内层 END。数据库不会报语法错,但结果错得隐蔽 —— 比如所有 type = 'refund' 的记录都进了默认分支。
- 每层
CASE独立写完再嵌套,不要边写边缩进;写完立刻检查CASE和END数量是否相等 - 用换行对齐结构:
CASE WHEN type = 'order'<br> THEN CASE WHEN status = 1 THEN '已下单'<br> ELSE '其他订单'<br> END<br> ELSE '非订单'<br>END
- 超过两层嵌套,建议拆成视图或 CTE,否则后期维护成本陡增
CASE 很难持续维护;字段含义变更、新增状态、多语言支持,都会让这段 SQL 快速变成技术债。










