推荐用 SUM(CASE WHEN ...) 而非 COUNT,因其能显式处理 NULL(通过 ELSE 0)、支持单行多条件并行统计、语义清晰且避免隐式过滤;COUNT(CASE...) 在含 ELSE 时失效,且可读性弱。

为什么用 SUM(CASE WHEN ...) 而不是 COUNT?
因为 COUNT 会把 NULL 当作不计数项,而 CASE WHEN 配合 SUM 能精准控制“只加 1 或加 0”,避免漏算性别字段为 NULL 的记录。更关键的是,它天然支持一行内并行统计多个条件(比如同时算男、女、未知),不用反复 JOIN 或子查询。
常见错误是写成 COUNT(CASE WHEN gender = '男' THEN 1 END)——这其实也能跑通,但语义上不如 SUM 直观,且当所有值都为 NULL 时,COUNT 返回 0,SUM 也返回 0,行为一致;但一旦混入 ELSE 0,COUNT 就失效了。
-
SUM(CASE WHEN gender = '男' THEN 1 ELSE 0 END):安全、明确、推荐 -
COUNT(CASE WHEN gender = '男' THEN 1 END):依赖隐式非空过滤,可读性弱 - 别漏写
ELSE 0,否则NULL不参与求和,结果偏小
部门+性别交叉统计的完整写法
核心是用 GROUP BY dept_name 搭配三个并列的 SUM(CASE...) 表达式,分别统计男、女、总数(用于算比例)。
SELECT
dept_name,
SUM(CASE WHEN gender = '男' THEN 1 ELSE 0 END) AS male_cnt,
SUM(CASE WHEN gender = '女' THEN 1 ELSE 0 END) AS female_cnt,
COUNT(*) AS total_cnt,
ROUND(
SUM(CASE WHEN gender = '男' THEN 1 ELSE 0 END) * 100.0 / NULLIF(COUNT(*), 0),
2
) AS male_pct,
ROUND(
SUM(CASE WHEN gender = '女' THEN 1 ELSE 0 END) * 100.0 / NULLIF(COUNT(*), 0),
2
) AS female_pct
FROM employees
GROUP BY dept_name;
-
NULLIF(COUNT(*), 0)是防除零的关键,比CASE WHEN COUNT(*) = 0 THEN NULL ELSE ... END更简洁 - 乘以
100.0(带小数点)确保结果为浮点数,避免整数除法截断(如 PostgreSQL/SQL Server 中5/10得 0) - 如果表里有
gender为 '未知' 或空字符串,需在CASE中显式覆盖,否则会进ELSE 0,导致总数对不上
遇到 gender 字段类型是数字或枚举怎么办?
别硬套字符串匹配。先查清实际取值:SELECT DISTINCT gender FROM employees; 再调整 CASE 条件。
- 若
gender是 tinyint:用WHEN gender = 1(假设 1=男) - 若用 ENUM('M','F'):注意大小写,写成
WHEN gender = 'M' - 若字段名是
sex或user_gender,别写错列名——这是线上最常见的拼写错误 - MySQL 8.0+ 支持
PIVOT,但兼容性差,绝大多数生产环境仍靠SUM(CASE...)扛住
性能和 NULL 值的真实影响
SUM(CASE...) 是单次扫描,比用多次 SELECT COUNT(...) WHERE gender=... 快得多,但前提是 dept_name 和 gender 上有联合索引(如 (dept_name, gender))。没有索引时,大表可能全表扫描,但这是统计类查询的常态,优化重点不在写法而在索引。
- NULL 值会被
ELSE 0拉进统计,所以male_cnt + female_cnt可能小于total_cnt——这反而是数据质量的真实反映 - 如果业务要求“只统计明确填写性别的人”,就把
COUNT(*)换成COUNT(gender),它自动忽略 NULL - 别在
CASE里写函数如UPPER(gender),会导致索引失效;清洗数据时统一转存标准值更稳妥
真正容易被忽略的是:比例分母是否该包含 NULL。很多报表默认剔除,但 HR 系统常要求“含未填项”的总人数作为基数——得跟业务方确认口径,代码只是忠实实现规则。










