count(*)统计所有行(含全NULL行),count(字段)仅统计该字段非NULL行;前者性能更优且是SQL标准,后者需读取字段并判空,仅在需统计非空值时使用。

count(*) 统计的是所有行,包括字段值为 NULL 的行
这是最根本的语义差异。COUNT(*) 不关心任何列的内容,只数“行存在与否”。哪怕整行所有字段都是 NULL,它也会被计入。而 COUNT(字段名) 只统计该字段值不为 NULL 的行 —— 它本质上是在做“非空校验 + 计数”两件事。
常见错误现象:
- 执行
SELECT COUNT(*), COUNT(email) FROM users;,结果两个数字不一致,就说明email列有NULL值 - 误以为
COUNT(id)和COUNT(*)等价,结果在id允许为NULL的表里得到更小的数值
count(字段) 会触发字段读取和 NULL 判断,性能通常更差
COUNT(*) 在 InnoDB 中通常走主键索引(或优化后的行计数路径),不取实际字段值;而 COUNT(字段名) 必须逐行读出该字段内容,再判断是否为 NULL。这意味着:
- 如果字段是
TEXT或JSON类型,开销明显更大 - 即使字段定义为
NOT NULL,MySQL 仍需确认约束有效性,无法完全跳过字段访问 - 没有 WHERE 条件时,
COUNT(*)往往比COUNT(字段)快 20%~50%,尤其在大表上
count(*) 是 SQL 标准,且 MySQL 对其做了深度优化
从 SQL92 开始,COUNT(*) 就是统计行数的唯一标准语法。MySQL 的优化器对它有特殊照顾:
- InnoDB 8.0.13+ 对无条件
COUNT(*)查询做了扫表路径优化,避免全字段解析 - MyISAM 表直接返回元数据中的行数缓存(但仅限无 WHERE 的简单查询)
- 阿里《Java开发手册》明确【强制】禁止用
COUNT(字段)或COUNT(1)替代COUNT(*)
注意:COUNT(1) 虽然功能等价,但它不是标准语法,且部分老版本 MySQL 会多一层常量构造开销 —— 没必要用。
什么时候必须用 count(字段)?
只有当你真正需要“某字段有效值的数量”时才用 COUNT(字段名),而不是“总行数”:
- 统计注册用户中填写了手机号的人数:
COUNT(phone) - 查订单表里已支付订单数(
pay_time非空表示已支付):COUNT(pay_time) - 配合
GROUP BY分组统计某字段非空出现次数
别为了“看起来更具体”而写 COUNT(id) —— 如果 id 是主键,它必然非空,此时 COUNT(*) 更语义清晰、更稳、更快。


















