SELECT COUNT 语句,你真的了解它吗?

说到 SELECT COUNT 相信大家都不陌生,因为在数据库的日常操作中,使用最频繁的就是查询操作;而在所有的查询操作中 COUNT 用到的次数,相信是最多的。

关于COUNT函数功能,无论是Oracle还是MySQL,都是一样的,那就是计数。

但是,就是这个常用的COUNT函数,却还有很多我们所不知道的功能。


1.什么是COUNT

关于COUNT函数,在官网中有详细介绍:

SELECT COUNT 语句,你真的了解它吗?

COUNT(expr) ,返回SELECT语句检索的行中expr的值不为NULL的数量。结果是一个BIGINT值。


2.COUNT(列名)、COUNT(常量)和COUNT(*)这三种用法一样吗

上面我们提到COUNT(expr)用于检索行数,统计的是expr不为NULL的数量,那么COUNT(列名)、 COUNT(常量) 和 COUNT(*)这三种语法中,expr分别为列名、 常量 和 *时,查询的结果是否一样呢?


在这三个条件中,常量 是一个固定值,肯定不为NULL;*可以理解为查询整行,所以肯定也不为NULL,那么就只有列名的查询结果有可能是NULL了。


所以, COUNT(常量) 和 COUNT(*)表示的是直接查询符合条件的数据库表的行数。而COUNT(列名)表示的是查询符合条件的列的值不为NULL的行数。


除了查询得到结果集有区别之外,COUNT(*)相比COUNT(常量) 和 COUNT(列名)来讲,COUNT(*)是SQL92定义的标准统计行数的语法,因为他是标准语法,所以MySQL数据库对他进行过很多优化。

<code>小备注:SQL92,是数据库的一个ANSI/ISO标准。它定义了一种语言(SQL)以及数据库的行为(事务、隔离级别等)。/<code>


3.数据库对COUNT(*)有优化吗

前面提到了COUNT(*)是SQL92定义的标准统计行数的语法,所以MySQL数据库对他进行过很多优化。那么,具体都做过哪些事情呢?


这里我们分MyISAM和InnoDB两种执行引擎来进行介绍。


MyISAM和InnoDB有很多区别,其中有一个关键的区别和我们接下来要介绍的COUNT(*)有关,那就是MyISAM不支持事务,MyISAM中的锁是表级锁;而InnoDB支持事务,并且支持行级锁。

因为MyISAM的锁是表级锁,所以同一张表上面的操作需要串行进行,所以,MyISAM做了一个简单的优化,那就是它可以把表的总行数单独记录下来,如果从一张表中使用COUNT(*)进行查询的时候,可以直接返回这个记录下来的数值就可以了,当然,前提是不能有where条件。

MyISAM之所以可以把表中的总行数记录下来供COUNT(*)查询使用,那是因为MyISAM数据库是表级锁,不会有并发的数据库行数修改,所以查询得到的行数是准确的。

但是,对于InnoDB来说,就不能做这种缓存操作了,因为InnoDB支持事务,其中大部分操作都是行级锁,所以可能表的行数可能会被并发修改,那么缓存记录下来的总行数就不准确了。

但是,InnoDB还是针对COUNT(*)语句做了些优化的。

在InnoDB中,使用COUNT(*)查询行数的时候,需要进行扫表,我们说的优化就是在扫表过程中进行的。


我们知道,COUNT(*)的目的只是为了统计总行数,所以根本不关心查到的具体值是什么,因此如果能够在扫表的过程中,选择一个成本较低的索引进行的话,那就可以大大节省时间。


我们知道,InnoDB中索引分为聚簇索引(主键索引)和非聚簇索引(非主键索引),聚簇索引的叶子节点中保存的是整行记录,而非聚簇索引的叶子节点中保存的是该行记录的主键的值。


相比之下,非聚簇索引要比聚簇索引小很多,所以MySQL会优先选择最小的非聚簇索引来扫表。所以,当我们建表的时候,除了主键索引以外,创建一个非主键索引还是有必要的。


<code>小总结:从MySQL 8.0.13开始,针对InnoDB的SELECT COUNT(*) FROM TABLE语句,确实在扫表的过程中做了一些优化。前提是查询语句中不包含WHERE或GROUP BY等条件。/<code>


4.COUNT(*)和COUNT(1)有什么区别


介绍完了COUNT(*),接下来看看COUNT(1),对于这二者到底有没有区别,网上有很多不同的说法。


有的说COUNT(*)执行时会转换成COUNT(1),所以COUNT(1)少了转换步骤,所以更快。


还有的说,因为MySQL针对COUNT(*)做了特殊优化,所以COUNT(*)更快。


那么,到底哪种说法是对的呢?首先我们看下MySQL官方文档是怎么说的:


<code>InnoDB handles SELECT COUNT(*) and SELECT COUNT(1) operations in the same way. 
There is no performance difference./<code>


翻译过来就是 InnoDB引擎对于两种查询的优化是一样的,所以根本不存在谁比谁快!


那既然COUNT(*)和COUNT(1)一样,建议用哪个呢?


建议使用COUNT(*)!因为这个是SQL92定义的标准统计行数的语法,本文只是针对mysql做了分析,对于存在的数据库,最好还是使用标准预发。


5.COUNT(字段) 能不能使用


对于COUNT(字段),它的查询就是进行全表扫描,然后判断指定字段的值是不是为NULL,不为NULL则累加。所以,如果是需要统计非NULL的值,还是需要使用这个语句的


总结


本文介绍了COUNT函数的用法,主要用于统计表行数。主要用法有COUNT(*)、COUNT(字段)和COUNT(固定值)。


三种查询里面,因为COUNT(*)是SQL92定义的标准统计行数的语法,所以MySQL对他进行了很多优化,MyISAM中会直接把表的总行数单独记录下来供COUNT(*)查询,而InnoDB则会在扫表的时候选择最小的索引来降低成本。当然,这些优化的前提都是没有进行where和group的条件查询。因此,如果不带查询条件的情况下,COUNT(*)性能是最好的。


分享到:


相關文章: