COUNT(*)统计所有符合条件的行,无视NULL;COUNT(col)仅统计col非NULL的行,二者结果差异源于语义判断逻辑不同,且COUNT(col)在InnoDB中因需逐行判断NULL可能慢5–10倍。

COUNT(*) 统计的是行是否存在,COUNT(col) 统计的是 col 是否非 NULL
这是两者结果不同的根本原因。MySQL 不是“数数据”,而是按语义做判断:COUNT(*) 只看某行是否被 WHERE 条件选中(哪怕所有列都是 NULL,只要这行存在,就算 1);而 COUNT(col) 必须读出 col 的值,再判断它是否为 NULL —— 是,则跳过;否,则累加。
常见错误现象:
- 明明
SELECT *看到 1000 行,COUNT(email)却返回 942 - 用
COUNT(id)和COUNT(*)对比,发现结果一致,就以为“没区别”,其实执行路径不同
使用场景提醒:
- 要统计“有多少用户注册了”,且
email字段允许NULL→ 用COUNT(email) - 要统计“用户表当前总记录数”,不管字段值 → 用
COUNT(*) - 想查“有多少用户填了手机号”,但
phone列定义为NOT NULL,实际却存了空字符串''→COUNT(phone)仍会把''当作非NULL计入,结果不准;此时应改用COUNT(CASE WHEN phone != '' THEN 1 END)
为什么 COUNT(col) 在 InnoDB 里可能慢很多?
InnoDB 不缓存总行数,所有 COUNT 都得扫描。但扫什么、怎么扫,差别很大:
-
COUNT(*)或COUNT(主键):优化器通常选最小索引(比如二级索引),只遍历索引树叶子节点,不回表 -
COUNT(col)且col允许NULL:必须访问完整行数据(因为NULL判断只能在聚簇索引/行记录里做),相当于强制全表扫描 -
COUNT(col)且col是NOT NULL:仍需逐行读取该列,但省去NULL判断开销;性能介于前两者之间
实测中,百万级表上 COUNT(email)(email 允 NULL)比 COUNT(*) 慢 5–10 倍很常见,不是配置问题,是引擎行为决定的。
COUNT(1) 和 COUNT(*) 到底有没有区别?
没有实质区别。MySQL 8.0+ 中,COUNT(1) 会被优化器等价重写为 COUNT(*),执行计划、扫描范围、耗时几乎完全一致。
容易踩的坑:
- 误以为
COUNT(1)“比COUNT(*)少解析字段所以更快” → 实际无差异,纯属历史误解 - 在带
WHERE条件的查询里写COUNT(1),以为能规避*的开销 → 条件过滤后,两者行为完全一样 - 看到老文档说 “MyISAM 下
COUNT(*)直接读元数据”,就推断 InnoDB 也如此 → InnoDB 从不缓存行数,无论用哪个形式都得算
真正影响性能的是是否带条件、索引覆盖程度、以及列是否允许 NULL,而不是括号里写 * 还是 1。
最常被忽略的一点:即使你用了 COUNT(*),只要加了 WHERE 条件,InnoDB 就无法跳过数据扫描 —— 它必须逐行判断是否满足条件。所谓“快”,仅针对无条件的全表计数。线上分页、状态统计等真实场景,往往绕不开条件,这时候优化重点早该转向索引和查询结构,而不是纠结括号里写啥。


















