COUNT(列名)、COUNT(常量)和COUNT(*)之间的区别
COUNT( * )和COUNT(1) :在InnoDB中COUNT(*)和COUNT(1)实现上没有区别,而且效率一样,统计结果中,会包含值为NULL的行数
COUNT(字段): 需要进行字段的非NULL判断,所以效率会低一些。
因为COUNT( * )是SQL92定义的标准统计行数的语法,并且效率高,所以没有某个字段不为null 的统计行数需求请直接使用COUNT( * )查询表的行数!
COUNT(id)和COUNT()以外,还可以使用COUNT(常量)(如COUNT(1))来统计行数,那么这三条SQL语句有什么区别呢?到底哪种效率更高呢?为什么《阿里巴巴Java开发手册》中强制要求不让使用 COUNT(列名)或 COUNT(常量)来替代 COUNT()呢?
1、COUNT(列名) ,返回SELECT语句检索的行中expr的值不为NULL的数量。结果是一个BIGINT值。
2、但是,值得注意的是,COUNT( * ) 和count(1)的统计结果中,会包含值为NULL的行数。
那么列名、 常量 和 *这三个条件中,常量 是一个固定值,肯定不为NULL。*可以理解为查询整行,所以肯定也不为NULL,那么就只有列名的查询结果有可能是NULL了。
所以, COUNT(常量) 和 COUNT( * )表示的是直接查询符合条件的数据库表的行数。而COUNT(列名)表示的是查询符合条件的列的值不为NULL的行数。
COUNT(*)是SQL92定义的标准统计行数的语法,因为他是标准语法,所以MySQL数据库对他进行过很多优化。
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(字段)
COUNT(字段),他的查询就比较简单粗暴了,就是进行全表扫描,然后判断指定字段的值是不是为NULL,不为NULL则累加。
相比COUNT(),COUNT(字段)多了一个步骤就是判断所查询的字段是否为NULL,所以他的性能要比COUNT()慢。
COUNT(*)的优化
MySQL中比较常用的执行引擎就是InnoDB和MyISAM。
MyISAM
不包含WHERE或GROUP BY等条件
MyISAM不支持事务,MyISAM中的锁是表级锁;而InnoDB支持事务,并且支持行级锁。
因为MyISAM的锁是表级锁,所以同一张表上面的操作需要串行进行,所以,MyISAM做了一个简单的优化,那就是它可以把表的总行数单独记录下来,如果从一张表中使用COUNT(*)进行查询的时候,可以直接返回这个记录下来的数值就可以了,当然,前提是不能有where条件。
MyISAM之所以可以把表中的总行数记录下来供COUNT(*)查询使用,那是因为MyISAM数据库是表级锁,不会有并发的数据库行数修改,所以查询得到的行数是准确的。
InnoDB
不包含WHERE或GROUP BY等条件
InnoDB来说,就不能做这种缓存操作了,因为InnoDB支持事务,其中大部分操作都是行级锁,所以可能表的行数可能会被并发修改,那么缓存记录下来的总行数就不准确了。
InnoDB中索引分为主键索引和非主键索引主键引的叶子节点中保存的是整行记录,而非主键索引的叶子节点中保存的是该行记录的主键的值。
所以,相比之下,非主键索引要比主键索引小很多,所以MySQL会优先选择最小的非主键索引来扫表。所以,当我们建表的时候,除了主键索引以外,创建一个非主键索引还是有必要的。