MySQL--count(*)countcount(id)count(field)比较

Posted 黄智霖-blog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL--count(*)countcount(id)count(field)比较相关的知识,希望对你有一定的参考价值。

注:环境为mysql 7 InnoDB引擎

测试

  首先创建一个测试表:

CREATE TABLE `test_table` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `num` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `I1` (`num`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

  test_table表中有两个索引,分别是主键索引和普通索引 I1,基于这样一个表结构我们来分别使用explain来看看查询计划。

  1. count(*)
explain select count(*) from test_table;


  可以看实际还是使用了I1索引,这个是查询优化器做出的选择,MySQL也并不会把所有数据都取出来,直接按行累加。

  1. count(id)
explain select count(id) from test_table;


  按照理解,count(id)应该走主键索引才对,但是查询优化器知道主键索引作为聚簇索引包含了所有字段内容,肯定比普通索引大,明显扫描I1索引效率更高,而且I1索引也有id字段,所以仍然使用了I1索引。

  1. count(1)
explain select count(1) from test_table;


  仍然使用的是I1索引,而且count(1)不用取出任何字段的值。

  1. count(field)

  这种情况就要看field字段有没有索引。本例中test_table表的num字段有索引,那么count(num)的执行计划如下:

explain select count(num) from test_table;


  这种情况虽然也走了I1索引,但是需要把num值取出来,如果为NULL不会加1,如果我们把num字段上的索引删除,再explain一下:


  这下没办法,只能全表扫描了,所以这种方式相对来说效率最低。

注:要比较效率,那么至少应该保证参与比较的sql完成的是同样的功能,结果也需要相同,但是本例中,count(字段)对于其它count来说,如果num允许为NULL,那么结果就可能不一样,所以这里只是从整体上做一个判断

结论

  • count(*):如果有辅助索引,查询优化器会选择合适的辅助索引,否则选择主键索引
  • count(1):同上,使用辅助索引(优先)或主键索引
  • count(id):同上,使用辅助索引(优先)或主键索引,因为辅助索引也有id字段
  • count(field):如果field有索引,那么能走索引,否则要全表扫描;即使能走索引,也要把field取出来判断其是否为空

  所以总体效率比较结果如下:count(*) ≈ count(1) ≈ count(id) > count(field)
  MyISAM每个表维护了行数,可以直接获取,但是InnoDB支持事务,基于MVCC不同的事务读到的行数可能不一样。除了通过count查询,还可以自己维护行数,比如将行数缓存到Redis,但是使用缓存就不可避免的会有一致性的问题。不过也可以将行数保存到数据库,依赖数据库的本地事务保证数据一致性,当然这只适用于查询全表行数的场景。某些时候我们可能只需要看一个表大致是个什么样的数据量,不需要非常精确的结果,那么可以通过以下语句达到目的:

show table status like tableName;

以上是关于MySQL--count(*)countcount(id)count(field)比较的主要内容,如果未能解决你的问题,请参考以下文章

MySql 执行countcount(*) 与 count(列名) 区别

面试必备:聊聊Mysql的count(*)countcount(column)的区别?

count(*)countcount(column)的区别

countcount(*)与count(列名)的执行区别

countcount(*)count(列名)的区别

MySQL查询count(*)countcount(field)的区别收集