写点什么

开源一夏 | count(列名)、 count(常量)、 count(*) 区别

  • 2022 年 8 月 30 日
    北京
  • 本文字数:2118 字

    阅读完需:约 7 分钟

开源一夏 | count(列名)、 count(常量)、 count(*)区别

写文背景

首先来说一下为什么要写这篇文章,因为作为一个开发者的话,日常开发过程中时一定会遇到统计数量相关的业务需求的,那么遇到这样的需求,就一定会遇到 mysql 的 count()函数的用法,当然,如果你直接查询列表对象,通过计算对象 list 的 size()也是可以的,就是有点没必要而已,哈哈。在开发过程中总是纠结于 count 时到底是用 count(列名)、 count(常量)、 count(*)其中的哪个,用哪个统计数据的效率会高些,每次开发每次去百度找前辈的经验介绍,但是每次得到的建议总是会有些差别,今天看到了一篇阿里关于 count 的文章,觉得挺好,在这里分享一下,顺便加上一些个人的使用建议。

Mysql 官网 count 介绍

首先我们来看一下 mysql 官网关于 count 的介绍

3.3.4.8 Counting RowsDatabases are often used to answer the question, “How often does a certain type of data occur in a table?” For example, you might want to know how many pets you have, or how many pets each owner has, or you might want to perform various kinds of census operations on your animals.
Counting the total number of animals you have is the same question as “How many rows are in the pet table?” because there is one record per pet. COUNT(*) counts the number of rows, so the query to count your animals looks like this:
复制代码

百度翻译:

数据库通常用于回答“某类数据在表中出现的频率”的问题,例如,您可能想知道您有多少宠物,或者每个主人有多少宠物,或者您可能想对您的动物执行各种普查操作。

计算你拥有的动物总数与“宠物表中有多少行?”的问题相同,因为每只宠物有一条记录。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(*),简单容易理解。


发布于: 刚刚阅读数: 4
用户头像

让技术不再枯燥,让每一位技术人爱上技术 2022.07.22 加入

还未添加个人简介

评论

发布
暂无评论
开源一夏 | count(列名)、 count(常量)、 count(*)区别_开源_六月的雨在infoQ_InfoQ写作社区