SQL分段统计最稳妥方式是CASE WHEN配合GROUP BY,需统一用左闭右开等边界风格,避免漏计或重复;应前置WHERE过滤、避免在CASE中用非SARGable表达式,并优先对原始字段建索引。

用 CASE WHEN 做数值区间分组统计,核心是写对条件边界
直接说结论:SQL 分段统计最稳妥的方式就是 CASE WHEN 配合 GROUP BY,但边界写错会导致数据漏计或重复计——比如用 和 <code>>= 100 并列,100 就会被两个分支同时匹配(取决于数据库是否支持多匹配,MySQL 默认只取第一个,PostgreSQL 则严格按顺序)。
常见错误现象:COUNT(*) 总数对不上原始表行数;某个区间结果为 0,但实际查数据发现存在落在该区间的值。
- 始终用「左闭右开」或「左开右闭」统一风格,推荐
score >= 60 AND score 这种写法,避免等号重复 - 必须包含
ELSE分支,否则NULL值(如空分数、未录入字段)会被直接丢弃,不参与任何分组 - 别在
CASE WHEN外层再套一层WHERE过滤原字段,否则会先筛掉数据再分段,导致区间统计基数变小
MySQL 和 PostgreSQL 的 CASE WHEN 行为差异要留意
MySQL 允许 CASE 表达式出现在 SELECT 列中不加 GROUP BY,但此时会隐式按整行分组(效果等同于没分组),而 PostgreSQL 会直接报错 column must appear in the GROUP BY clause。所以跨数据库写法要收敛到标准 SQL。
使用场景:你正在写一个兼容 MySQL/PG 的报表脚本,不能依赖方言特性。
- 所有用于分组的
CASE WHEN必须显式写进GROUP BY子句,不要用别名(有些旧版 MySQL 不支持GROUP BY alias) - PostgreSQL 对
NULL比较更严格,score = NULL永远为 false,必须用score IS NULL;MySQL 虽然允许= NULL语法,但语义不一致,建议统一用IS NULL - 如果字段类型是字符串但存数字(如
'95'),MySQL 可能自动转,PG 会报错,务必提前CAST(score AS INTEGER)
性能关键:别让 CASE WHEN 阻碍索引使用
当表有千万级数据,又在 WHERE 里写了 CASE WHEN score > 85 THEN 'A' END = 'A',这条语句基本无法走索引——因为 CASE 是计算列,优化器没法下推条件。
正确做法是把区间逻辑前置到 WHERE,再用 CASE 做标签:
SELECT
CASE
WHEN score >= 90 THEN 'A'
WHEN score >= 80 THEN 'B'
ELSE 'C'
END AS level,
COUNT(*)
FROM exam_result
WHERE score IS NOT NULL -- 先过滤,减少扫描量
GROUP BY
CASE
WHEN score >= 90 THEN 'A'
WHEN score >= 80 THEN 'B'
ELSE 'C'
END;- 如果常查某几个固定区间,建议在业务写入时就存一个
level字段,避免每次查询都计算 - 对
score建单独索引即可,不需要函数索引(除非你真用了WHERE ABS(score) > 50这类表达式) - 注意:MySQL 8.0+ 支持函数索引,但
CASE WHEN不能作为函数索引表达式,只能用确定性标量函数如ABS()、YEAR()
时间字段做分段统计,别直接比字符串
很多人用 CASE WHEN create_time LIKE '2024-01%' THEN 'Jan',这会让索引完全失效,且跨年时逻辑难维护。
正确方式是用日期函数归一化后再分段:
SELECT
CASE
WHEN YEAR(create_time) = 2024 AND MONTH(create_time) IN (1,2,3) THEN 'Q1'
WHEN YEAR(create_time) = 2024 AND MONTH(create_time) IN (4,5,6) THEN 'Q2'
ELSE 'Other'
END AS quarter,
COUNT(*)
FROM orders
GROUP BY quarter;- MySQL 的
QUARTER()、PostgreSQL 的EXTRACT(QUARTER FROM create_time)更简洁,但要注意时区——如果字段是TIMESTAMP类型,MySQL 会按系统时区转,PG 按字段定义时区转 - 别用
DATE_FORMAT(create_time, '%Y-%m')做分组键,字符串比较慢,且无法利用日期索引 - 如果要按「最近7天」「上周」「上月」动态分段,
CASE WHEN写死日期不现实,应改用UNION ALL或应用层拼接条件
分段统计真正难的不是语法,而是边界定义是否覆盖全集、NULL 是否被有意忽略、以及大表下计算逻辑有没有悄悄干掉索引。这些地方一旦出问题,数据看不出来错,但报表值已经偏了。

















