一、三种写法分别是什么意思
1.COUNT(*)
SELECT COUNT(*) FROM users;表示:
统计结果集中的行数,不管这一行的字段是否为
NULL。
这是最标准、最清晰的“统计行数”写法。
2.COUNT(1)
SELECT COUNT(1) FROM users;1是一个永远不为NULL的常量。
对于结果集中的每一行,MySQL 都可以得到一个非NULL的1,所以最终统计的仍然是行数:
每行 -> 计算出 1 -> 计数加一因此,对 InnoDB 来说:
COUNT(*) COUNT(1)MySQL 官方明确说明,这两种写法处理方式相同,没有性能差异。
3.COUNT(主键字段)
SELECT COUNT(id) FROM users;它统计的是:
id字段不为NULL的行数。
如果id是真正的主键,那么主键必然是NOT NULL,因此:
COUNT(*) COUNT(1) COUNT(id)通常得到相同结果。
二、举个例子
表中有三行:
| id | name | remark |
|---|---|---|
| 1 | 张三 | 正常 |
| 2 | 李四 | NULL |
| 3 | 王五 | 正常 |
执行:
SELECT COUNT(*) AS c1, COUNT(1) AS c2, COUNT(id) AS c3, COUNT(remark) AS c4 FROM users;结果是:
c1 = 3 c2 = 3 c3 = 3 c4 = 2原因:
COUNT(*):统计所有行
COUNT(1):1永不为NULL,统计所有行
COUNT(id):id是主键,全部非NULL
COUNT(remark):忽略remark IS NULL的那一行
MySQL 中,COUNT(expr)统计的是表达式不为NULL的数量,而COUNT(*)统计行数。
三、哪个性能最好?
对 InnoDB 来说
通常结论是:
COUNT(*) = COUNT(1)性能基本相同。
如果主键字段是:
id BIGINT PRIMARY KEY那么:
COUNT(id)结果通常也相同,但没有必要为了性能特意使用它。
实际开发中推荐:
SELECT COUNT(*) FROM users;原因是:
语义最清晰
表达的是“统计行数”
MySQL 对它有明确的优化路径
不会因为字段是否允许NULL而产生误差
MySQL 官方说明,InnoDB 会按照当前事务可见性统计记录,并且对于没有额外WHERE、GROUP BY的COUNT(*),通常会遍历可用的最小二级索引;没有二级索引时才扫描聚簇索引。
所以实际性能主要取决于:
是否有合适的WHERE条件
是否有合适的索引
索引大小
数据是否已经在 Buffer Pool 中
当前事务可见的数据量
表中数据规模
而不是取决于*、1还是主键字段。
四、一个容易出错的场景:LEFT JOIN
考虑:
SELECT u.id, COUNT(*), COUNT(o.id) FROM users u LEFT JOIN orders o ON u.id = o.user_id GROUP BY u.id;假设用户张三没有订单。
LEFT JOIN仍然会保留张三这一行,只是订单表字段全部为NULL:
u.id o.id 1 NULL因此:
COUNT(*) = 1 COUNT(o.id) = 0这里两者含义不同:
COUNT(*):统计连接结果中的行数
COUNT(o.id):统计真正匹配到订单的数量
因此统计子表数量时,经常应该写:
COUNT(o.id)而不是:
COUNT(*)五、MyISAM 的特殊情况
如果使用 MyISAM,且查询是:
SELECT COUNT(*) FROM users;没有WHERE条件时,MyISAM 可以利用存储的行数信息快速返回结果。
但 InnoDB 不保存一个可以直接返回的精确总行数,因为不同事务可能看到不同的数据版本。
不过现在绝大多数业务表使用 InnoDB,所以通常记住 InnoDB 的结论即可。