因为case when未写else时默认返回null,而sum会跳过null导致该行不参与求和,使结果为null而非0,造成数据“消失”;实操必须显式写else 0,并确保group by覆盖所有需保留维度。

为什么直接写 SUM(CASE WHEN) 会漏数据?
因为 CASE WHEN 没有 ELSE 时默认返回 NULL,而 SUM() 会跳过 NULL —— 这本身没错,但如果你漏写了 ELSE 0,看起来像“没统计”,其实是“该行没参与求和”。尤其在多条件分组时,某类条件不匹配就彻底消失,导致列值为 NULL 而非 0,后续计算或展示容易出错。
实操建议:
- 每个
CASE WHEN必须显式写ELSE 0,哪怕逻辑上“不该进 else” - 确认
GROUP BY字段覆盖所有需要保留的维度,否则聚合后行数变少 - 用
COALESCE(SUM(...), 0)是冗余的——只要CASE里写了ELSE 0,SUM结果就不可能是NULL
怎么写才能让不同条件对应不同列?
核心是:每个目标列对应一个独立的 SUM(CASE WHEN ... THEN value ELSE 0 END)。不是嵌套,是并列。常见错误是试图在一个 CASE 里写多个条件分支来生成多列,那是行不通的。
比如把销售记录按产品类型拆成三列(phone、laptop、tablet):
SELECT region, SUM(CASE WHEN product_type = 'phone' THEN amount ELSE 0 END) AS phone, SUM(CASE WHEN product_type = 'laptop' THEN amount ELSE 0 END) AS laptop, SUM(CASE WHEN product_type = 'tablet' THEN amount ELSE 0 END) AS tablet FROM sales GROUP BY region;
注意点:
-
THEN后必须是数值型字段(如amount),不能是字符串或表达式(除非你确定它可隐式转数字) - 条件顺序不影响结果,但建议按业务重要性或字母序排列,方便维护
- 如果
product_type有空值,CASE不匹配任何WHEN,走ELSE 0,所以空值不会污染列
遇到 NULL 值或模糊匹配怎么办?
CASE WHEN 对 NULL 判断要小心:col = NULL 永远为 FALSE,得用 IS NULL;模糊匹配要用 LIKE,且注意大小写和尾部空格。
示例:统计含 “pro” 的型号(忽略大小写)、纯数字订单号、以及未知类型:
SELECT status, SUM(CASE WHEN UPPER(model) LIKE '%PRO%' THEN qty ELSE 0 END) AS pro_model, SUM(CASE WHEN order_id ~ '^[0-9]+$' THEN qty ELSE 0 END) AS numeric_order, -- PostgreSQL 正则 SUM(CASE WHEN type IS NULL OR TRIM(type) = '' THEN qty ELSE 0 END) AS unknown_type FROM orders GROUP BY status;
关键提醒:
- MySQL 用
REGEXP,PostgreSQL 用~,SQL Server 用LIKE配合通配符,语法差异大,别照搬 -
TRIM(type) = ''比type = ''更安全,防空格干扰 - 正则或函数调用会拖慢性能,大数据量时优先考虑加索引或预处理字段
为什么 GROUP BY 缺一不可,又常被写错?
漏写 GROUP BY 会导致全表聚合成一行,或者报错(严格模式下);多写了无关字段,会让本该合并的行被强行拆开。最典型的是:想按日期+地区汇总,却只写了 GROUP BY date,结果每个地区每天变成多行,而不是每天一行含多个地区列。
检查要点:
-
SELECT中所有**非聚合字段**(即没包在SUM、COUNT等里的)都必须出现在GROUP BY中 - 如果用了别名(如
region AS area),GROUP BY必须用原始字段名,不能用area(多数数据库不支持) - 时间字段如
created_at要先截断再分组,例如DATE(created_at)或TO_CHAR(created_at, 'YYYY-MM-DD'),否则秒级精度会让同一天变多行
真正难的不是语法,是厘清“哪几个维度要保留为行,哪些条件要展开为列”——这一步想错,后面全白搭。另外,当条件超过 5–6 个,SUM(CASE WHEN) 语句会变得极难读,这时候该考虑用 PIVOT(SQL Server / Oracle)或 CTE 预处理,而不是硬扛。











