CASE WHEN能避免多次扫描,因为它是在单次表扫描中对每行即时计算的标量表达式,配合聚合函数可并行统计多维度指标;而多个独立查询或UNION ALL需重复I/O。

为什么CASE WHEN能避免多次扫描?
因为 CASE WHEN 是 SQL 中的标量表达式,它在每行数据上即时计算,不改变行数。聚合函数(如 SUM、COUNT)作用于它返回的结果时,整个查询仍只需一次全表扫描——而用多个子查询或 UNION ALL 拼接不同维度统计,则必然触发多次扫描或重复 I/O。
CASE WHEN + 聚合函数的标准写法
关键在于把条件逻辑“内嵌”到聚合函数内部,而不是写在 WHERE 或外层。常见错误是写成 WHERE status = 'paid' 再套 COUNT(*),这只能算单一条件;要并行统计多个状态,必须:
-
SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END)—— 计数用SUM加 0/1 更稳妥,比COUNT(CASE ...)少踩空值坑 -
COUNT(CASE WHEN amount > 100 THEN order_id END)—— 这里用COUNT(字段)会自动忽略NULL,等价于只统计满足条件的非空值 - 所有
CASE WHEN分支必须有ELSE(哪怕写ELSE NULL),否则隐式补NULL可能在某些数据库中引发意外聚合行为
容易被忽略的 NULL 和类型对齐问题
两个高频翻车点:
-
CASE WHEN各分支返回值类型不一致时,数据库会隐式转换——比如一个分支返回INT,另一个返回VARCHAR,可能触发全列类型提升,拖慢性能甚至报错(如 PostgreSQL 严格检查) - 当条件列本身含
NULL,status = 'paid'判断结果为UNKNOWN,不会进THEN,但也不会进ELSE(除非显式写WHEN status IS NULL)。所以统计“非 paid”不能只靠ELSE,得明确写WHEN status != 'paid' OR status IS NULL - 聚合前没加
COALESCE处理NULL,导致最终结果字段为NULL(例如SUM(CASE ...)全不匹配时返回NULL,而非0)
真实场景示例:订单表一次查出各状态+金额分段统计
假设表 orders 有 status('pending'/'paid'/'shipped'/'cancelled')和 amount 字段:
SELECT COUNT(*) AS total, SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END) AS paid_cnt, SUM(CASE WHEN status = 'shipped' THEN amount ELSE 0 END) AS shipped_amt, COUNT(CASE WHEN amount >= 500 THEN order_id END) AS big_orders FROM orders;
这里四个指标全部基于单次扫描完成。注意:shipped_amt 用 SUM(... ELSE 0) 避免 NULL 累加中断;big_orders 用 COUNT(字段) 自动过滤不满足条件的行,比 SUM(CASE ... THEN 1 ELSE 0) 更直白。
多维度交叉统计(比如“已支付且金额超500的订单数”)也一样套进去:SUM(CASE WHEN status = 'paid' AND amount > 500 THEN 1 ELSE 0 END)——只要逻辑能写进一个 CASE 表达式,就 still 单次扫描。
真正复杂的地方不在语法,而在想清楚哪些维度需要正交统计、哪些条件存在隐式 NULL 分支、以及目标数据库对混合类型的容忍度。

















