基于InnoDB引擎
1. count(*)
统计满足条件的总行数 ,会统计包含NULL的行,不会忽略null。
MySQL优化层做了专门优化,不会读取行的全部数据,只统计行是否存在。
2. count(1)
传入常量1,每一行都不为null,效果等价统计总行数,同样统计全部行数,不会忽略NULL。
早期版本有性能差异,现在MySQL优化后,count(*) 和 count(1) 性能几乎没有差别。
3. count(字段名)
统计该字段不为NULL的行数,如果这个字段的值是NULL,这一行会被过滤掉,不参与计数。
示例:
| id | name |
|---|---|
| 1 | 张三 |
| 2 | NULL |
| 3 | 李四 |
sql
select count(*) from user; --结果:3,统计所有行
select count(1) from user; --结果:3
select count(name) from user; --结果:2,name为null那一行被排除
底层执行差异
-
count(*)
InnoDB会找最小的二级索引来扫描,不需要读取聚簇索引完整行,只统计行数,效率很高。
-
count(1)
优化器内部会转化成和count()相近逻辑,遍历索引,每行填个1做计数,现代版本性能和count()基本一致。
-
count(字段)
- 如果字段有索引:扫描这个二级索引,判断字段值是否为NULL,非NULL才计数;
- 如果字段没有索引:只能扫描聚簇索引(全表),读取每一行这个字段的值做判断,性能很差。
⚠️注意:
count(主键id):id不会为null,统计全部行数。但会读取主键字段的值,性能略低于count(*)。
常见误区
- 网上说count(1)比count(*)快,旧版本,现在不成立,MySQL已经做优化;
- count(字段)会跳过NULL,业务很容易踩坑,如果要统计行数,不要用count(普通字段);
- MyISAM不带where条件的count(*)会直接返回表元数据,速度极快;InnoDB没有这个特性,必须扫描索引统计行数,因为MVCC多版本,不同事务看到行数不一样。
总结:
- 统计行数优先使用
count(*),MySQL专门优化,性能最优; count(1)结果和count(*)一致,新版本性能基本无差别;count(字段)只统计字段非NULL的数据,如果字段允许为null,统计结果会少一部分;无索引时性能很差。