最常用且兼容性最好的方法是SELECT COUNT() AS total, COUNT(CASE WHEN status = 'active' THEN 1 END) AS active_count FROM users;COUNT()统计所有行,COUNT(CASE...)仅统计满足条件的非NULL行,避免用ELSE 0或SUM替代以保证语义清晰和跨数据库兼容。

用 COUNT(*) 和 COUNT(CASE WHEN ...) 一次查出两个数
直接在 SELECT 中并列写 COUNT(*) 和带条件的 COUNT(CASE WHEN ... THEN 1 END),就能在同一行结果里拿到总数和条件计数。这是最常用、兼容性最好(MySQL/PostgreSQL/SQL Server/Oracle 都支持)的方法。
注意:别用 COUNT(1) 或 COUNT(column_name) 替代条件部分——COUNT() 只统计非 NULL 值,而 CASE WHEN 不匹配时默认返回 NULL,刚好被忽略,这正是我们想要的行为。
-
COUNT(*)统计所有行(包括含 NULL 的行) -
COUNT(CASE WHEN status = 'active' THEN 1 END)只统计status = 'active'的行 - 不要写成
COUNT(CASE WHEN status = 'active' THEN 1 ELSE 0 END)—— 这样会把ELSE 0的 0 当作有效值计入,结果就错了
SELECT COUNT(*) AS total, COUNT(CASE WHEN status = 'active' THEN 1 END) AS active_count FROM users;
WHERE 子句不能替代条件计数
如果只想要满足条件的记录数,用 WHERE 没问题;但想同时要总数和条件数,就不能靠 WHERE 分两次查——那样得发两条 SQL,还可能因并发写入导致两个结果不一致(比如中间有新用户注册)。
更糟的是,有人试图用子查询拼接:
SELECT (SELECT COUNT(*) FROM users) AS total,
(SELECT COUNT(*) FROM users WHERE status = 'active') AS active_count;
这看似可行,但在大表上性能差(全表扫描两次),且某些数据库(如早期 MySQL)对同一表的多子查询可能加锁冲突。单次扫描 + CASE WHEN 是更稳更快的选择。
用 SUM 替代 COUNT(CASE ...) 也行,但语义稍弱
SUM(CASE WHEN status = 'active' THEN 1 ELSE 0 END) 和上面的 COUNT(CASE ...) 效果一样,数值结果完全相同。
区别在于语义:COUNT 强调“计数”,SUM 强调“求和”。虽然执行计划往往一致,但可读性和意图表达上,COUNT 更贴切。另外,如果误写成 SUM(CASE WHEN ... THEN 1 END)(漏掉 ELSE 0),NULL 会被 SUM 忽略,结果依然正确——但这属于侥幸,不推荐依赖。
- 推荐写法:
COUNT(CASE WHEN condition THEN 1 END) - 避免写法:
SUM(CASE WHEN condition THEN 1 END)(没ELSE,逻辑隐晦) - 别用布尔表达式直接进 SUM,比如
SUM(status = 'active')—— 这在 MySQL 里能运行(TRUE=1, FALSE=0),但在 PostgreSQL 或 SQL Server 上会报错
GROUP BY 场景下同样适用
如果需要按分组统计总数和条件数(比如每个部门的员工总数和其中 active 员工数),只需加上 GROUP BY dept_id,其他结构不变。
这时要注意:所有非聚合字段都必须出现在 GROUP BY 中,否则会报错(尤其在 PostgreSQL 和 SQL Server 严格模式下)。别漏掉分组字段,也别把 COUNT() 类字段错误地放进 GROUP BY。
SELECT dept_id, COUNT(*) AS dept_total, COUNT(CASE WHEN status = 'active' THEN 1 END) AS dept_active FROM users GROUP BY dept_id;
条件计数里的字段(如 status)不需要单独出现在 GROUP BY 中——它只是计算逻辑的一部分,不是分组依据。

















