COUNT()和COUNT(1)性能完全一致,因优化器将其等价转换;COUNT()语义明确、标准兼容,应优先使用;真正影响性能的是表结构、索引和查询条件。

COUNT(*) 和 COUNT(1) 在现代数据库中性能完全一致,没有“哪个更好”的选择空间。
COUNT(*) 与 COUNT(1) 的执行计划一模一样
- 你用
EXPLAIN或查看实际执行计划(如 MySQL 的EXPLAIN FORMAT=TRADITIONAL、SQL Server 的执行计划 XML)会发现:两者生成的计划节点、扫描方式、I/O 次数、CPU 开销全部相同。 - 原因很简单:优化器在解析阶段就识别出
COUNT(1)中的1是非空常量,等价于“每行都计 1 次”,和COUNT(*)的语义完全重合。 - 所有主流引擎(MySQL InnoDB、PostgreSQL、SQL Server、Oracle 12c+)都做此等价转换,不会为
COUNT(1)多做任何列读取或判断。
常见错误现象:
- 看到老博客说“
COUNT(1)不用解析 *,所以更快” → 这是 Oracle 8i 时代的过时认知,现代优化器早就不这么干了。 - 在慢查询日志里看到
COUNT(1)耗时略低 → 实际是网络抖动、缓存命中、并发干扰等偶然因素,不是函数本身差异。
COUNT(*) 更值得优先使用,因为语义明确且标准兼容
-
COUNT(*)是 SQL92 标准定义的“统计行数”唯一规范写法,所有数据库都保证其行为一致。 -
COUNT(1)属于实现细节层面的等价替代,但容易引发误解(比如新人误以为它在“检查某列是否为 1”)。 - 阿里巴巴《Java 开发手册》明确【强制】:不要用
COUNT(1)或COUNT(列名)替代COUNT(*)。
使用场景注意点:
- 如果你在 MyISAM 表上跑
COUNT(*)且无WHERE条件,它会直接返回元数据缓存值,快得离谱;COUNT(1)同样享受该优化。 - 如果表是 InnoDB,两者都需扫描最小索引(通常是主键),速度取决于索引大小和缓冲池状态,跟写法无关。
COUNT(列名) 才是真正要小心的性能陷阱
-
COUNT(id)(id是主键)可能比COUNT(*)略快 —— 仅当优化器决定走二级索引且该索引更小(极少见)。 -
COUNT(name)(name允许 NULL 且无索引)会触发全表扫描 + 每行判空,明显更慢。 - 即使
name字段加了索引,数据库仍需遍历索引确认哪些条目非 NULL(索引不存 NULL,但需验证覆盖性),不如COUNT(*)直接扫聚簇索引干脆。
参数差异关键点:
-
COUNT(*)统计物理行存在性,不管任何列内容。 -
COUNT(列名)统计该列非 NULL 值数量,行为依赖列定义(NOT NULL约束能帮优化器跳过判空,但不保证跳过扫描)。
真正影响性能的从来不是写 COUNT(*) 还是 COUNT(1),而是表结构、索引设计、WHERE 条件是否存在、以及是否在事务活跃期对大表做无条件统计。把精力放在这些地方,比纠结括号里写啥实在得多。


















