必须先用case when将数值映射为离散标签再group by,不可直接在group by中写表达式;推荐用floor(age/10)*10等算术分桶替代冗长when,注意负数处理差异及null兜底。

用 CASE WHEN 划分自定义区间再 GROUP BY
直接在 GROUP BY 里写表达式会出错,必须先用 CASE WHEN 把数值映射成离散标签,再按这个标签分组。比如想把年龄分成「0-17」「18-24」「25-34」「35+」四档:
-
CASE WHEN age BETWEEN 0 AND 17 THEN '未成年' WHEN age BETWEEN 18 AND 24 THEN '青年' WHEN age BETWEEN 25 AND 34 THEN '中青年' ELSE '其他' END—— 注意BETWEEN是闭区间,ELSE必须兜底,否则NULL会被单独归为一组 - 别在
SELECT里重复写整段CASE,应定义列别名(如AS age_group),然后GROUP BY age_group,否则部分数据库(如 MySQL 5.7 严格模式)会报错 - 区间边界重叠或遗漏会导致数据丢失,建议用
SELECT DISTINCT age先探查实际取值范围
用数字分桶(bucket)替代字符串标签更省事
如果只是想按固定宽度切分(比如每 10 岁一档),用整除运算比写一堆 WHEN 更简洁可靠:
-
FLOOR(age / 10) * 10 AS bucket_start得到每档起点(0,10,20…),再拼出区间描述:CONCAT(FLOOR(age / 10) * 10, '-', FLOOR(age / 10) * 10 + 9) - 注意负数处理:若字段可能为负(如温度),
FLOOR在不同数据库行为不一致,PostgreSQL 和 SQL Server 用FLOOR,MySQL 可能需改用TRUNCATE(age / 10, 0) * 10 - 这种算术分桶无法表达非等宽区间(如「0-17」「18-24」),硬套会导致逻辑错误
WHERE 和 HAVING 对分组统计的影响容易混淆
过滤动作的位置决定结果是否包含空组:
- 想只看「用户数 ≥ 100 的年龄段」,用
HAVING COUNT(*) >= 100—— 它作用于分组后结果 - 想排除测试账号(
user_type = 'test')再统计,必须写在WHERE子句,否则这些记录仍参与分组计数 - 如果用了
CASE WHEN分组但某区间无数据,默认不会出现在结果里;要强制显示(如「35+」组为 0),得用LEFT JOIN预定义区间表,不是简单加OR 1=1
窗口函数不能替代 GROUP BY 做区间计数
有人试图用 COUNT(*) OVER (PARTITION BY ...) 实现类似效果,这是误解:
-
OVER是为每行附加一个聚合值,不减少行数,结果行数 = 原表行数,而分组统计要求输出每组一行 - 若混用
GROUP BY和窗口函数,需确保PARTITION BY的表达式与GROUP BY一致,否则逻辑错乱 - 真要同时看明细和分组汇总,才用窗口函数;单纯计数,
GROUP BY+COUNT(*)是唯一正解
区间定义和数据分布必须匹配,比如用 BETWEEN 18 AND 24 却漏掉 age IS NULL 的情况,这部分记录就会消失在统计外。











