订阅更新

SQL CASE WHEN 有哪些常见坑?

面试问 074 约 3 分钟读完 1 次阅读

本文目录
  1. 面试怎么回答
  2. 第一匹配原则决定最终结果
  3. NULL 不能用等号判断
  4. 边界重叠和边界空洞
  5. 返回值类型不要靠隐式转换兜底
  6. 聚合里最容易造成口径重叠
  7. 生产中怎么验证?

面试怎么回答

CASE WHEN 按顺序返回第一个命中的分支,所以条件要从具体到宽泛,并检查区间重叠和空洞。NULL 用 IS NULL 判断,最好显式写 ELSE;各分支返回类型要兼容。用于聚合时还要先明确是互斥分桶还是包含指标,否则多个 SUM(CASE...) 可能重复统计。上线前我会用临界值和 NULL 样本验证,并核对分类合计。

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

详细讲解

先说一个很容易被忽略的结果:同一个高价值用户,明明满足“消费大于 1 万”,最后却被分进了“消费大于 1 千”的普通高消费组。

SQL 没报错,数据类型也正常,问题只出在 CASE WHEN 的顺序。

CASE WHEN 看起来只是条件判断,但生产里最常见的问题不是不会写,而是条件重叠、空值漏接、类型混乱和指标重复统计。

第一匹配原则决定最终结果

CASE 会按顺序判断条件,并返回第一个满足条件的分支,后面的分支不再执行。

CASE
  WHEN amount >= 1000  THEN '高消费'
  WHEN amount >= 10000 THEN '核心客户'
  ELSE '普通客户'
END

amount = 20000 在第一行就命中,因此永远到不了“核心客户”。正确思路是把更具体、更严格的条件放前面:

CASE
  WHEN amount >= 10000 THEN '核心客户'
  WHEN amount >= 1000  THEN '高消费'
  ELSE '普通客户'
END

CASE 第一命中顺序

NULL 不能用等号判断

这段代码不会按预期命中空值:

WHEN channel = NULL THEN '未知'

SQL 的 NULL 表示未知,应该写 channel IS NULL。另外,如果省略 ELSE,所有未命中的记录通常会返回 NULL。分类字段一旦出现这类空值,下游 GROUP BY 很容易多出一个解释不清的分组。

边界重叠和边界空洞

区间分类要同时检查两件事:是否有一条记录能命中多个条件,以及是否存在任何条件都接不住的值。

例如 score >= 80 和 score BETWEEN 60 AND 80 在 80 分处重叠;如果改成 score > 80 与 score < 80,80 分又掉进空洞。最稳妥的方式是利用从上到下的顺序,只写必要边界,并准备 59、60、79、80、NULL 等临界测试样本。

返回值类型不要靠隐式转换兜底

CASE 各分支最终要合并为一个可兼容的数据类型。一个分支返回数字、另一个返回字符串,不同引擎可能报错,也可能隐式转换,给后续比较和排序埋坑。

如果业务需要数字编码,就所有分支都返回数字;需要标签,就都返回字符串。不要让 ELSE 0 和 THEN 'unknown' 混在一起。

聚合里最容易造成口径重叠

SUM(CASE WHEN amount >= 1000  THEN 1 ELSE 0 END) AS high_cnt,
SUM(CASE WHEN amount >= 10000 THEN 1 ELSE 0 END) AS core_cnt

这里“核心客户”同时属于“高消费”,两个指标不是互斥分类。如果报表要展示漏斗,这可能正确;如果要展示加总等于总人数的分组,就会重复。

CASE WHEN 避坑检查表

所以写 CASE 前要先问:这些条件是互斥分桶、包含关系,还是多个独立标签?语法相同,指标语义完全不同。

生产中怎么验证?

我通常会输出原始字段、CASE 结果和每个条件的布尔命中标记,用边界样本做对照;再统计各分类数量、NULL 数和分类总数是否等于总体。对于金额等核心口径,还要抽取重叠命中记录,确认它是业务允许还是条件错误。

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