COUNT(1)与COUNT(*)性能完全相同,差异在±0.3ms内;真正影响性能的是WHERE条件索引、列是否NOT NULL、表是否有主键及COUNT(列名)的NULL判断开销。

COUNT(1) 并不比 COUNT(*) 更快——在 MySQL 8.0+、PostgreSQL、SQL Server 等主流数据库中,两者执行计划完全一致,耗时差异通常在 ±0.3ms 内,波动主要来自 buffer pool 命中率,而非写法本身。
EXPLAIN 显示两者的执行路径一模一样
执行 EXPLAIN SELECT COUNT(*) FROM orders 和 EXPLAIN SELECT COUNT(1) FROM orders,你会看到 type、key、rows、Extra 字段完全相同。这是因为优化器在词法解析阶段就将 COUNT(1) 折叠为 COUNT(*),后续走的都是同一套“行存在性计数”逻辑:只遍历索引叶子节点(如主键 B+ 树或最小二级索引),不读取任何列值,连 record body 都不加载。
- 所谓“
COUNT(1)省去字段读取”是误解——COUNT(*)同样不读字段 - 所谓“
COUNT(*)要扫描所有列”纯属混淆了SELECT *的投影行为 - MyISAM 下的元数据直查已无参考价值,InnoDB 表占比超 99%,且不维护全局行数
COUNT(列名) 才是真正的性能分水岭
一旦括号里填的是具体列(如 COUNT(email)),语义就变了:必须逐行判断该列是否为 NULL。这会触发额外开销:
- 即使
email有唯一索引,优化器仍需从索引项中取出值判空,无法绕过解码 - 若该列允许
NULL且无索引,就会强制全表扫描 + 每行解码,I/O 和 CPU 开销比COUNT(*)高 20%~50% - 在
LEFT JOIN后用COUNT(right_table.id),实际统计的是关联成功数,语义偏移且更慢
真正影响 COUNT 性能的只有两个硬条件
括号里填什么,对速度几乎没影响;拖慢查询的从来不是写法,而是底层约束与索引设计:
- WHERE 条件有没有对应索引?例如
COUNT(*) WHERE status = 'active'必须有INDEX idx_status (status)才能走索引扫描 - 被统计的列是否定义了
NOT NULL?如果没加约束,优化器不敢跳过判空,再窄的索引也白搭 - 表有没有主键?没有主键的 InnoDB 表无法利用最小索引优化,只能扫整棵主键树
最容易被忽略的一点:加了 WHERE 后,COUNT(*) 和 COUNT(1) 依然等价,但 COUNT(列名) 的 NULL 判断发生在过滤之后——这意味着它的结果可能和你想统计的“该列非空总数”完全不是一回事。

















