COUNT(1)与COUNT()在现代数据库中性能完全等价,优化器在语义分析阶段即识别1为非空常量,统一重写为COUNT()并生成相同执行计划;所谓“COUNT(1)更快”源于过时版本或误判。

在现代数据库(MySQL 8.0+、PostgreSQL 14+、SQL Server、Oracle 12c+)中,COUNT(1) 和 COUNT(*) 执行计划完全一致,性能差异可忽略——这不是“差不多”,而是优化器在语义分析阶段就将两者归一化为同一内部操作。
优化器在解析时就做了等价重写
数据库不会真去“算 1”,也不会真去展开 *;它识别出 COUNT(1) 中的 1 是一个非空常量表达式,立刻推导出语义等价于“统计行存在性”,并统一转为 COUNT(*) 的内部表示。你用 EXPLAIN 看到的 type、key、rows 字段几乎完全相同,不是巧合,是设计使然。
- MySQL 8.0+ 的解析器对
*有专用快速路径,比解析数字常量还轻量 - 旧版 MySQL(5.5 及更早)中所谓
COUNT(1)更快,实为绕过COUNT(*)handler 接口的缓存 bug,属于缺陷而非优势 - PostgreSQL 12 之前在窗口函数嵌套场景下,
COUNT(1)曾产生意外空值,COUNT(*)更稳定
COUNT(列名) 才是真正的性能分水岭
COUNT(*) 和 COUNT(1) 都走「存在性扫描」:只遍历最小索引(如主键 B+ 树叶子节点),不读数据页、不判 NULL、不回表。而 COUNT(email) 必须检查每行 email 是否为 NULL,这就触发了根本差异:
- 若
email允许NULL且无索引 → 强制全表扫描 + 列解码,比COUNT(*)慢 20%~40% - 若
email有二级索引但 NULL 占比高(如 60%)→ 索引虽跳过 NULL,但页数更多、I/O 更高 - 若
id是NOT NULL主键,COUNT(id)可能和COUNT(*)等价,但这是因约束,不是因写法
真正影响 COUNT 性能的三个硬条件
别在括号里填什么上纠结——影响耗时的是底层支撑条件:
- 表有没有主键?InnoDB 强制要求主键,没主键的表
COUNT(*)可能退化为全表扫描 - WHERE 条件列是否命中覆盖索引?例如
COUNT(*) WHERE status = 'active'要快,得有INDEX idx_status (status) - 执行时缓冲池(buffer pool)是否热?实测中同一查询两次耗时差 ±0.5ms,常比写法差异还大
最容易被忽略的一点:NULL 处理是 SQL 标准强制行为,不可绕过。哪怕字段 99% 非空,只要没加 NOT NULL 约束,优化器就无法跳过判空逻辑——这才是 COUNT(列名) 慢的根源,和 1 还是 * 无关。


















