条件聚合必须用case when,因where在分组前过滤整行会丢失数据,而case when可在同一组内按不同条件分别统计;sum和count处理null方式不同,需谨慎选择else策略。

条件聚合必须用 CASE WHEN,不能直接 WHERE
WHERE 是在分组前过滤整行,会把其他状态的记录直接踢掉;而条件聚合是要在同一组里按不同条件分别统计,比如“已支付订单金额”和“待支付订单金额”必须共存于同一结果行。硬套 WHERE 会导致漏统计或语法报错:ERROR 1140: Mixing of GROUP columns with no GROUP columns is illegal。
-
CASE WHEN是唯一通用写法,所有主流数据库(MySQL、PostgreSQL、SQL Server、Oracle)都支持 - 别用
IF()(MySQL 专属),跨库迁移时会崩 -
COUNT(IF(...))看似简洁,但IF返回 NULL 时COUNT才计数,逻辑易反——推荐统一用SUM(CASE WHEN ... THEN 1 ELSE 0 END),语义更直白
SUM 和 COUNT 在条件聚合里的行为差异
表面都是“数数”,但底层处理 NULL 的方式不同,直接影响结果:
-
SUM(CASE WHEN status='paid' THEN amount ELSE 0 END):把非已支付订单的amount强制设为 0,再求和 —— 安全,但注意 0 会拉低平均值(如果后续算 AVG) -
SUM(CASE WHEN status='paid' THEN amount END):不写ELSE默认返回 NULL,SUM自动跳过 NULL —— 更干净,推荐 -
COUNT(CASE WHEN status='paid' THEN 1 END):只对已支付行返回 1,其余为 NULL,COUNT只计非 NULL —— 正确 -
COUNT(CASE WHEN status='paid' THEN 1 ELSE 0 END):错误!因为0是非 NULL 值,会被计入总数
多条件嵌套和 NULL 处理的坑
真实业务常要组合多个维度,比如“华东区高价值客户(客单价 ≥ 500)的已支付订单数”。这时容易忽略两点:
- 嵌套
CASE WHEN必须对齐层级:CASE WHEN region='East' AND amount >= 500 AND status='paid' THEN 1 END,别拆成两层CASE,否则逻辑断裂 - 涉及平均值时,千万别用
ELSE 0:AVG(CASE WHEN weekday THEN response_time END)—— 这样 NULL 被自然过滤;若写成ELSE 0,就把周末响应时间全拉低了 - 当字段本身可能为 NULL(如
discount_rate),先用COALESCE(discount_rate, 0)再进条件判断,否则discount_rate > 0.1对 NULL 行永远不成立
GROUP BY 场景下避免 HAVING 误用
条件聚合本身是 SELECT 子句里的计算,和 HAVING 无关。常见错误是想“只显示已支付订单数 > 10 的部门”,然后写:
SELECT dept, SUM(CASE WHEN status='paid' THEN 1 ELSE 0 END) AS paid_cnt FROM orders GROUP BY dept HAVING paid_cnt > 10;
这能跑通,但效率差——HAVING 是在聚合后过滤,而真正该提前过滤的是原始数据。更优写法是:
SELECT dept, COUNT(*) AS paid_cnt FROM orders WHERE status = 'paid' GROUP BY dept HAVING COUNT(*) > 10;
关键区别:WHERE status = 'paid' 减少了参与分组的行数,尤其表大时性能提升明显。条件聚合用于“同组多口径统计”,WHERE 用于“缩小统计范围”——两者分工要清楚。










