CASE WHEN必须嵌入SELECT等合法上下文,不可单独执行;最常用在SELECT中做字段值映射,需写ELSE防NULL,分支结果类型须一致,WHERE中滥用会导致索引失效。
CASE WHEN 必须写在 SELECT 列表里,不能单独执行
SQL 没有“直接返回文案”的独立语句,所有条件映射都得靠 CASE WHEN 表达式嵌在 SELECT 中生成新字段。它不是控制流语句,不改变查询结构,只负责把原始值转成你想要的业务文案。
常见错误现象:
- 写
CASE WHEN status = 'paid' THEN '已支付'却漏掉ELSE,结果 pending/cancelled 全变成NULL,前端显示为空白 - 混用类型:
WHEN amount > 100 THEN '大单'和ELSE 0→ 报错CASE types text and integer cannot be matched - 用简单形式硬套范围判断:
CASE status WHEN >= 90 THEN 'A'→ 语法错误,简单形式只认=
正确写法要点:
- 一律用搜索型
CASE WHEN condition THEN ...,支持IS NULL、>=、LIKE等任意判断 - 每个
THEN和ELSE后的值必须是同一类型,字符串就全用字符串,推荐显式写ELSE '未知' - 分支顺序很重要:把高频或高优先级条件放前面,比如
WHEN status = 'paid'比WHEN status = 'pending'更常查,就往前放
WHERE 里别用 CASE WHEN 做条件过滤
想实现“VIP 用户查近30天订单,普通用户查全部”,很多人会写:
WHERE CASE WHEN user_type = 'vip' THEN order_date >= DATE_SUB(NOW(), INTERVAL 30 DAY) ELSE 1=1 END
这语法虽能跑通,但基本等于放弃索引。数据库优化器无法为这种表达式生成有效执行计划,大概率触发全表扫描。
真正高效的做法是用布尔逻辑重写:
SQL数据库备份恢复助手 V2.8.8 官方绿色安全版
SQL数据库备份恢复助手 V2.8.8 官方绿色安全版
下载
WHERE (user_type = 'vip' AND order_date >= DATE_SUB(NOW(), INTERVAL 30 DAY)) OR user_type != 'vip'
或者更清晰地拆成两个条件组合,确保 user_type 和 order_date 上的索引都能被利用。
聚合统计时 CASE WHEN 必须配 ELSE 0
统计“已支付订单数”时,如果只写:
SUM(CASE WHEN status = 'paid' THEN 1 END)
那 status 不是 'paid' 的行,CASE 返回 NULL,而 SUM(NULL) 仍为 NULL,不是 0。这会导致整个聚合结果不可靠——尤其在和其它字段一起 GROUP BY 时,容易让整行数据消失。
安全写法是:
SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END)
同理,COUNT(CASE WHEN ... THEN 1 END) 会忽略 NULL,但如果你要的是“总行数中满足条件的比例”,就得用 SUM(... ELSE 0) 配合 COUNT(*) 手动算。
最易被忽略的一点:CASE WHEN 的所有分支结果类型必须一致,且数据库对隐式转换很敏感。比如 PostgreSQL 会严格拒绝 THEN 'yes' 和 ELSE 0 并存;MySQL 虽能转,但可能把字符串截成空或把数字转成字符串再拼接,导致意外结果。宁可多敲几个 CAST(... AS VARCHAR),也别赌隐式转换靠谱。