开源一夏 | count(列名)、 count(常量)、 count(*) 区别
写文背景
首先来说一下为什么要写这篇文章,因为作为一个开发者的话,日常开发过程中时一定会遇到统计数量相关的业务需求的,那么遇到这样的需求,就一定会遇到 mysql 的 count()函数的用法,当然,如果你直接查询列表对象,通过计算对象 list 的 size()也是可以的,就是有点没必要而已,哈哈。在开发过程中总是纠结于 count 时到底是用 count(列名)、 count(常量)、 count(*)其中的哪个,用哪个统计数据的效率会高些,每次开发每次去百度找前辈的经验介绍,但是每次得到的建议总是会有些差别,今天看到了一篇阿里关于 count 的文章,觉得挺好,在这里分享一下,顺便加上一些个人的使用建议。
Mysql 官网 count 介绍
首先我们来看一下 mysql 官网关于 count 的介绍
百度翻译:
数据库通常用于回答“某类数据在表中出现的频率”的问题,例如,您可能想知道您有多少宠物,或者每个主人有多少宠物,或者您可能想对您的动物执行各种普查操作。
计算你拥有的动物总数与“宠物表中有多少行?”的问题相同,因为每只宠物有一条记录。COUNT(*)计算行数,因此用于计算动物数的查询如下所示:
Mysql count 介绍地址:Counting Rows
阿里社区 Java 开发手册强制
作为 Java 开发者,最熟悉的应该就是阿里云社区的 Java 开发者手册,手册关于 count 的
阿里社区 Java 开发手册规定不能用 count(列名)、 count(常量)来替代 count(*)。
count(列名)、 count(常量)、 count(*)区别
说了以上的这么多背景,下面来说一下这三个的区别:
COUNT(常量) 和 COUNT(*)表示的是直接查询符合条件的数据库表的行数。而 COUNT(列名)表示的是查询符合条件的列的值不为 NULL 的行数。
除了查询得到结果集有区别之外,COUNT(*)相比 COUNT(常量) 和 COUNT(列名)来讲,COUNT(*)是 SQL92 定义的标准统计行数的语法,因为他是标准语法,所以 MySQL 数据库对他进行过很多优化。
COUNT(*)的优化
这里的介绍要区分不同的执行引擎。MySQL 中比较常用的执行引擎就是 InnoDB 和 MyISAM。
MyISAM 和 InnoDB 有很多区别,其中有一个关键的区别和我们接下来要介绍的 COUNT(*)有关,那就是 MyISAM 不支持事务,MyISAM 中的锁是表级锁;而 InnoDB 支持事务,并且支持行级锁。
因为 MyISAM 的锁是表级锁,所以同一张表上面的操作需要串行进行,所以,MyISAM 做了一个简单的优化,那就是它可以把表的总行数单独记录下来,如果从一张表中使用 COUNT(*)进行查询的时候,可以直接返回这个记录下来的数值就可以了,当然,前提是不能有 where 条件。
MyISAM 之所以可以把表中的总行数记录下来供 COUNT(*)查询使用,那是因为 MyISAM 数据库是表级锁,不会有并发的数据库行数修改,所以查询得到的行数是准确的。
但是,对于 InnoDB 来说,就不能做这种缓存操作了,因为 InnoDB 支持事务,其中大部分操作都是行级锁,所以可能表的行数可能会被并发修改,那么缓存记录下来的总行数就不准确了。
但是,InnoDB 还是针对 COUNT(*)语句做了些优化的。
在 InnoDB 中,使用 COUNT(*)查询行数的时候,不可避免的要进行扫表了,那么,就可以在扫表过程中下功夫来优化效率了。
从 MySQL 8.0.13 开始,针对 InnoDB 的 SELECT COUNT(*) FROM tbl_name 语句,确实在扫表的过程中做了一些优化。前提是查询语句中不包含 WHERE 或 GROUP BY 等条件。
我们知道,COUNT(*)的目的只是为了统计总行数,所以,他根本不关心自己查到的具体值,所以,他如果能够在扫表的过程中,选择一个成本较低的索引进行的话,那就可以大大节省时间。
我们知道,InnoDB 中索引分为聚簇索引(主键索引)和非聚簇索引(非主键索引),聚簇索引的叶子节点中保存的是整行记录,而非聚簇索引的叶子节点中保存的是该行记录的主键的值。
所以,相比之下,非聚簇索引要比聚簇索引小很多,所以 MySQL 会优先选择最小的非聚簇索引来扫表。所以,当我们建表的时候,除了主键索引以外,创建一个非主键索引还是有必要的。
至此,我们介绍完了 MySQL 数据库对于 COUNT(*)的优化,这些优化的前提都是查询语句中不包含 WHERE 以及 GROUP BY 条件。
COUNT(*)和 COUNT(1)
对于 COUNT(1)和 COUNT(*),MySQL 的优化是完全一样的,根本不存在谁比谁快!
那既然 COUNT(*)和 COUNT(1)一样,建议用哪个呢?
建议使用 COUNT(*)!因为这个是 SQL92 定义的标准统计行数的语法.
COUNT(字段)
COUNT(字段),查询过程就是进行全表扫描,然后判断指定字段的值是不是为 NULL,不为 NULL 则累加。
相比 COUNT(*),COUNT(字段)多了一个步骤就是判断所查询的字段是否为 NULL,所以他的性能要比 COUNT(*)慢。
建议
如果没有业务特殊的需求,count 统计时剔除某一字段为 null 的数据,那么推荐使用 count(*)。我个人在开发过程中基本也都是使用 count(*),简单容易理解。
版权声明: 本文为 InfoQ 作者【六月的雨在infoQ】的原创文章。
原文链接:【http://xie.infoq.cn/article/682ff7e18d4b9f4eb6d5831ba】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论