目录
MyISAM引擎表
康特星count(*)
孤独的count(*)
含其他列的康特星count(*)
含where子句的康特星count(*)
康特伊count(1)
孤独的count(1)
count(name)
count(distinct name)
InnoDB引擎表
康特星count(*)
孤独的count(*)
康特伊count(1)
孤独的count(1)
本文主要讨论MySQL中count(*)和count(1)的一些区别。实验基于如下MySQL版本:
先创建一张无任何索引的MyISAM引擎表myisam_test,表创建语句如下:
DROP TABLE IF EXISTS myisam_test; CREATE TABLE `myisam_test` ( name varchar(50) DEFAULT NULL, age int DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
准备4条测试数据(其中有两条相同),如下:
insert into myisam_test (name,age) values (null,null), ('zhangsan',23), ('',null), ('zhangsan',23);
SELECT * FROM myisam_test;
如图:
执行不含其他列且没有where子句的count(*),查询语句如下:
select count(*) from myisam_test;
查询到4条记录,如图:
发现无论是null值或者值相同的行都计算在内,结果返回了表的所有行数。我们添加explain后,查看该查询语句的执行计划如下:
explain select count(*) from myisam_test;
从执行计划中可以看到,type列的值null,Extra列的值为Select tables optimized away,意思是“选择最优化的表”,因为存储引擎MyISAM存储了当前表的准确的行数,可以非常快速的直接访问,无需扫描记录。
我们根据name字段分组统计,如下:
select name, count(*) from myisam_test group by name;
执行结果如下:
和预期一样,我们如下查看执行计划,如下:
结果很糟糕,首先进行了全表扫描,并且使用了临时表和排序,性能低下。
执行如下查询语句
select count(*) from myisam_test where name='zhangsan';
结果如图:
查看执行计划,如下:
可以看到进行了全表扫描,使用where子句进行了过滤,性能也比较低。
执行不包含其他列和where子句的count(1),查询语句如下:
select count(1) from myisam_test;
执行结果如图:
继续查看执行计划,如图:
发现和康特星无任何区别。
综上所述,对于MyISAM引擎表,康特星和康特伊并无区别。
如果我们将星号替换成列,看结果如何?执行如下查询,统计name的数量:
select count(name) from myisam_test;
执行结果如图:
原表中总共4条记录,统计结果为3条,忽略了name为null的记录。
如图,查看执行计划:
发现count(name)也是全表扫描。
查询语句如下:
select count(distinct name) from myisam_test;
执行结果如图:
从结果来看, 忽略了name值为null的记录,且多个name值相同的记录仅计数1次。执行计划如下:
前面我们认识了MyISAM引擎表,现在我们来看下无任何索引的InnoDB引擎表innodb_test,创建语句如下:
drop table if exists innodb_test; CREATE TABLE `innodb_test` ( `name` varchar(50) DEFAULT null, age int DEFAULT null ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
准备4条测试数据(其中有两条相同),如下:
insert into innodb_test(name,age) values (null,null), ('zhangsan',23), ('',null), ('zhangsan',23);
select * from innodb_test;
如图:
执行如下查询
select count(*) from innodb_test;
结果如图:
查看执行计划,如图
康特星走的是全表扫描。那么为什么不和MyISAM引擎一样,存储表的总行数直接返回呢?因为InnoDB是事务性存储引擎,并发事务可能同时“看到”不同的行数。因此,SELECT COUNT(*)语句只计数当前事务可见的行。 关于事务隔离可参见本文作者其他文章,此处不累述。
执行如下查询语句
select count(1) from innodb_test;
结果如图:
查看执行计划:
发现查询走的是全表扫描。
综上所述,总结如下:
无任何索引的MyISAM引擎表 | 无任何索引的InnoDB引擎表 | |
---|---|---|
count(*) | 返回表总行数; 直接返回引擎存储的行数; | 返回表总行数; 全表扫描; |
含其他列count(*) | 返回该列各个值的行数; 全表扫描,创建临时表和排序; | 返回该列各个值的行数; 全表扫描; |
含where子句count(*) | 返回符合where条件的行数; 全表扫描,使用where过滤; | 返回符合where条件的行数; 全表扫描,使用where过滤; |
count(name) | 返回name不为null的总行数; 全表扫描; | 返回name不为null的总行数; 全表扫描; |
count(distinct name) | 返回name不为null且不同的总行数; 全表扫描; | 返回name不为null且不同的总行数; 全表扫描; |
count(1) | 同count(*) | 同count(*) |
含其他列count(1) | 同count(*) | 同count(*) |
含where子句count(1) | 同count(*) | 同count(*) |
本文未讨论表含有索引的情况。