count(1) 会统计null值,因为1 != null
count(*) 会统计null值
count(field) 若field为null,则不会统计
在分析三个count之前,我们需要知道,一张表在没有主键索引的情况,mysql底层还是为表设置一个隐藏索引,这是由innodb的ibd文件的
物理特性决定的。在知道上面这点之后,故我们知道,无论一张表是只有一个主键索引还是没有任何索引,都是会去遍历聚簇索引的,这
就导致需要扫描所有的记录。
情况1 一张表是只有一个主键索引或没有任何索引且field允许为空
count(field)<count(1)=count(*),count(1)和count(*)不必多说都需要遍历全表且统计null值,count(field)效率低是因为field
允许为空,则在遍历聚簇索引的时候,mysql还需要去判断field是否为空,造成了额外开销
情况2 一张表是只有一个主键索引或没有任何索引且field不允许为空
count(field)=count(1)=count(*),count(field)与前两者效率相等是因为,这次遍历,不再需要对field进行空值判断了
情况3 一张表除了主键索引外,还有其他索引
假设有一张表
CREATE TABLE `pet` (
`id` bigint,
`name` varchar(255),
`age` int,
PRIMARY KEY (`id`),
KEY `idx_name` (`name`)
);
这时,mysql优化器当统计记录数的时候,会面临两个索引选择 primary key和idx_name
当count(*),count(1),count(id),count(name),mysql优化器走的都是idx_name索引,因为这个成本最低,那么原因是什么呢?
一个聚簇索引,数据页中,每条记录都包括着该记录的所有字段,既然一行记录变大了,该数据页总大小不变默认为16KB,故能容纳的
记录条数就变少了,则需要更多的数据页来存放记录,造成了额外的IO一个二级索引,数据页中,每条记录只包括着该记录的某几个字段,
例如idx_name索引,数据页中的一条记录只包括着name和id字段,一条记录变小了,则数据页能容纳的记录条数就变多了则只需要更少的
数据页来存放记录,减少了IO
所以当面临两个索引可以走的时候,mysql大部分情况下会选择二级索引来统计记录数,除了某些field在二级索引中并不存在,
例如count(age),表中没有age对应的二级索引,故mysql还是会去扫描全表的
结论:
上边儿的分析只针对innodb,大部分情况下,count(*)和count(1)总是相等的,count(field)效率可能<=前边儿两者,
因为field可能不存在索引或field允许为空需要造成额外的空值判断;
myisam中,count(*)永远效率最高,因为myisam底层维护着一个count变量来记录记录数,只有count(*)才能触发;
结合上边儿来看,count(*)是最好的选择
mysql count(*) count(1) count(具体字段)的解析
于 2023-09-05 13:01:42 首次发布