用 CASE WHEN 配合 SUM 可一行实现时间段横向对比,如“上月 vs 本月”订单金额、“工作日 vs 周末”活跃数;避免多次表扫描、不依赖索引、逻辑集中易维护,CASE 必须在 SUM 内且每分支返回数值(如 ELSE 0)。

用 CASE WHEN 配合 SUM 做时间段横向对比,比 GROUP BY 更直接
想看「上个月 vs 本月」的订单总金额、「工作日 vs 周末」的用户活跃数,别急着写两个子查询再 JOIN。用 CASE WHEN 把时间条件转成列,SUM 直接聚合,一行 SQL 拉出对比结果。
关键不是“能不能”,而是“为什么选这个写法”:它避免了多次扫描表、不依赖日期字段是否索引、逻辑集中易维护。
-
CASE WHEN必须放在聚合函数(如SUM)内部,不能写在WHERE或外部;否则会过滤掉其他时段数据 - 每个
CASE分支要确保返回数值类型(比如补ELSE 0),否则SUM可能返回NULL - 时间范围表达式别硬写字符串,优先用
date >= '2024-05-01' AND date ,避免 <code>BETWEEN的边界陷阱
SUM(CASE WHEN ...) 和 COUNT(CASE WHEN ...) 别混用
统计「满足条件的记录数」用 COUNT,统计「满足条件的某字段加总」才用 SUM。新手常在这里翻车:
- 想算「本月下单用户数」→ 用
COUNT(DISTINCT CASE WHEN ... THEN user_id END),不是SUM - 想算「本月订单总金额」→ 用
SUM(CASE WHEN ... THEN amount ELSE 0 END),不是COUNT -
COUNT对NULL不计数,所以CASE中没匹配到时必须写ELSE NULL(默认就是),不能写ELSE 0,否则会多计 0 值行
时间字段类型不一致会导致 CASE WHEN 全部失效
常见错误:created_at 是 TIMESTAMP,但 CASE 里拿 DATE(created_at) = '2024-05-01' 比较——看着对,实际可能因时区或精度丢数据。
- 统一用区间判断更稳:
created_at >= '2024-05-01' AND created_at - 如果字段是字符串(如
'20240501'),先用TO_DATE或STR_TO_DATE转成日期类型再比较,别用LIKE或子串截取 - PostgreSQL 用户注意:
date_trunc('month', created_at)比EXTRACT(YEAR FROM ...)+EXTRACT(MONTH FROM ...)组合更准,避免跨年计算错乱
性能敏感时,CASE WHEN 要配合索引字段写
这个写法本身不慢,慢是因为没走索引。如果 CASE 里的条件字段没索引,或者用了函数(如 YEAR(created_at)),全表扫描就来了。
- 把时间条件写成可下推的形式:
created_at BETWEEN '2024-05-01' AND '2024-05-31',而不是MONTH(created_at) = 5 - 复合索引优先包含
WHERE过滤字段,再放CASE用到的字段;比如查「不同城市+不同月份」,索引建在(city, created_at)上比单列有效得多 - MySQL 8.0+ 支持函数索引,真要用
DATE(created_at),可以显式建INDEX idx_date ON t (DATE(created_at))
CASE WHEN 写得再漂亮,结果也是错的。

















