从新认识MySQL中的COUNT语句

在数据库的增删改查操做中,使用最频繁的就是查询操做。html

而在全部查询操做中,统计数量操做更是常常被用到。数据库

关于数据库中行数统计,不管是MySQL仍是Oracle亦或者是SqlServer,都有一个函数可使用,那就是COUNT。缓存

而对于COUNT,有几个问题很值得去思考:网络

一、COUNT有几种用法?并发

二、COUNT(字段名)和COUNT()的查询结果有什么不一样?函数

三、COUNT(1)和COUNT()之间有什么不一样?性能

四、COUNT(1)和COUNT()之间的效率哪一个更高?优化

五、为何《阿里巴巴Java开发手册》建议使用COUNT()spa

六、MySQL的MyISAM引擎对COUNT()作了哪些优化?code

七、MySQL的InnoDB引擎对COUNT()作了哪些优化?

八、上面提到的MySQL对COUNT()作的优化,有一个关键的前提是什么?

九、SELECT COUNT() 的时候,加不加where条件有差异吗?

十、COUNT()、COUNT(1)和COUNT(字段名)的执行过程是怎样的?

若是以上10道题,所有准确无误的回答的话,那说明你真的很了解COUNT函数了,若是有哪些知识点是不了解的,那么本文正好能够从新帮你认识一下Count,也为数据库优化作一些思考。

认识COUNT

关于COUNT函数的介绍:

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

二、若是查询结果没有命中任何记录,则返回0

三、可是,值得注意的是,COUNT() 的统计结果中,会包含值为NULL的行数。

例:

create table #bla(id int,id2 int)
insert #bla values(null,null)
insert #bla values(1,null)
insert #bla values(null,1)
insert #bla values(1,null)
insert #bla values(null,1)
insert #bla values(1,null)
insert #bla values(null,null)

使用语句count(),count(id),count(id2)查询结果以下:

select count(),count(id),count(id2)
from #bla
results 7 3 2

除了COUNT(id)和COUNT()之外,还可使用COUNT(常量)(如COUNT(1))来统计行数,那么这三条SQL语句有什么区别呢?

到底哪一种效率更高呢?

为何《阿里巴巴Java开发手册》中强制要求不让使用 COUNT(列名)或 COUNT(常量)来替代COUNT()呢?

从新认识MySQL中的COUNT语句

COUNT(列名)、COUNT(常量)和COUNT()之间的区别

前面咱们提到过COUNT(expr)用于作行数统计,统计的是expr不为NULL的行数,那么COUNT(列名)、 COUNT(常量) 和 COUNT()这三种语法中,expr分别是列名、 常量 和 。

那么列名、 常量 和 这三个条件中,常量是一个固定值,确定不为NULL。

能够理解为查询整行,因此确定也不为NULL,那么就只有列名的查询结果有多是NULL了。

因此, COUNT(常量) 和 COUNT()表示的是直接查询符合条件的数据库表的行数。

而COUNT(列名)表示的是查询符合条件的列的值不为NULL的行数。

除了查询获得结果集有区别以外,COUNT()相比COUNT(常量) 和 COUNT(列名)来说,COUNT()是SQL92定义的标准统计行数的语法,由于他是标准语法,因此MySQL数据库对他进行过不少优化。

SQL92,是数据库的一个ANSI/ISO标准。

它定义了一种语言(SQL)以及数据库的行为(事务、隔离级别等)。

COUNT()的优化

前面提到了COUNT()是SQL92定义的标准统计行数的语法,因此MySQL数据库对他进行过不少优化。

那么,具体都作过哪些事情呢?

这里的介绍要区分不一样的执行引擎。

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 tblname语句,确实在扫表的过程当中作了一些优化。

前提是查询语句中不包含WHERE或GROUP BY等条件。

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

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

因此,相比之下,非聚簇索引要比聚簇索引小不少,因此MySQL会优先选择最小的非聚簇索引来扫表。

当咱们建表的时候,除了主键索引之外,建立一个非主键索引仍是有必要的。

至此,咱们介绍完了MySQL数据库对于COUNT()的优化,这些优化的前提都是查询语句中不包含WHERE以及GROUP BY条件。

COUNT()和COUNT(1)

介绍完了COUNT(),接下来看看COUNT(1),对于,这两者到底有没有区别,网上的说法众说纷纭。

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

还有的说,由于MySQL针对COUNT()作了特殊优化,因此COUNT()更快。

那么,到底哪一种说法是对的呢?

看下MySQL官方文档是怎么说的:

InnoDB handles SELECT COUNT() and SELECT COUNT(1) operations in the same way. There is no performance difference.

画重点:

same way , no performance difference。

因此,对于COUNT(1)和COUNT(),MySQL的优化是彻底同样的,根本不存在谁比谁快!

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

建议使用COUNT()!

由于这个是SQL92定义的标准统计行数的语法,并且本文只是基于MySQL作了分析,关于Oracle中的这个问题,也是众说纷纭的呢。

COUNT(字段)

最后,就是咱们一直还没提到的COUNT(字段),他的查询就比较简单粗暴了,就是进行全表扫描,而后判断指定字段的值是否是为NULL,不为NULL则累加。相比COUNT(),COUNT(字段)多了一个步骤就是判断所查询的字段是否为NULL,因此他的性能要比COUNT()慢。

总结

本文介绍了COUNT函数的用法,主要用于统计表行数。

主要用法有COUNT()、COUNT(字段)和COUNT(1)。

由于COUNT()是SQL92定义的标准统计行数的语法,因此MySQL对他进行了不少优化,MyISAM中会直接把表的总行数单独记录下来供COUNT()查询,而InnoDB则会在扫表的时候选择最小的索引来下降成本。固然,这些优化的前提都是没有进行where和group的条件查询。

在InnoDB中COUNT()和COUNT(1)实现上没有区别,并且效率同样,可是COUNT(字段)须要进行字段的非NULL判断,因此效率会低一些。

由于COUNT()是SQL92定义的标准统计行数的语法,而且效率高,因此请直接使用COUNT()查询表的行数!*

本文来自 木庄网络博客> 从新认识MySQL中的COUNT语句
相关文章
相关标签/搜索