在数据库的增删改查操作中,使用最频繁的就是查询操作。
而在所有查询操作中,统计数量操作更是经常被用到。
关于数据库中行数统计,无论是MySQL还是Oracle亦或者是SqlServer,都有一个函数可以使用,那就是COUNT。
而对于COUNT,有几个问题很值得去思考:
1、COUNT有几种用法?
2、COUNT(字段名)和COUNT()的查询结果有什么不同?
3、COUNT(1)和COUNT()之间有什么不同?
4、COUNT(1)和COUNT()之间的效率哪个更高?
5、为什么《阿里巴巴Java开发手册》建议使用COUNT()
6、MySQL的MyISAM引擎对COUNT()做了哪些优化?
7、MySQL的InnoDB引擎对COUNT()做了哪些优化?
8、上面提到的MySQL对COUNT()做的优化,有一个关键的前提是什么?
9、SELECT COUNT() 的时候,加不加where条件有差别吗?
10、COUNT()、COUNT(1)和COUNT(字段名)的执行过程是怎样的?
如果以上10道题,全部准确无误的回答的话,那说明你真的很了解COUNT函数了,如果有哪些知识点是不了解的,那么本文正好可以重新帮你认识一下Count,也为数据库优化做一些思考。
认识COUNT
关于COUNT函数的介绍:
1、COUNT(expr) ,返回SELECT语句检索的行中expr的值不为NULL的数量。结果是一个BIGINT值。
2、如果查询结果没有命中任何记录,则返回0
3、但是,值得注意的是,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()呢?
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等条件。