今天,我们讨论一个MySQL查询相关的经典问题,也是大家日常工作经常会用到的。不少同学可能使用了多年的COUNT(*),也不是很清楚它与COUNT(1)、COUNT(某一列)的区别,让我们今天就一探究竟吧。
面试官:COUNT(*)、COUNT(1)、COUNT(某一列)的区别是什么?哪个性能更好?
我们还是先用一张图概述所有要点:
基本概念
1、COUNT(*):
这个表达式会计算表中的所有行,包括那些值为NULL的行。它不考虑列的选择,直接统计整个表的行数。
MySQL官网[1]中有说明:InnoDB通过遍历最小的可用二级索引来处理SELECT COUNT(*)语句,除非索引或优化器提示优化器使用不同的索引。如果二级索引不存在,InnoDB通过扫描聚簇索引来处理SELECT COUNT(*)语句。
为什么要采用遍历最小的可用二级索引,而不是直接选聚簇索引?
因为二级索引不存储具体数据,相比聚簇索引占用空间小,遍历的I/O效率更高。
2、COUNT(1):
这是一个优化过的计数方式,实际上并不计算任何列的值,而是把“1”作为一个常量来对待,统计的是满足条件的行数。这种方式避免了访问表的具体列,理论上可以提高效率,尤其是在列值可为空的情况下。
MySQL官网[1]中有说明:InnoDB处理SELECT COUNT(*)和SELECT COUNT(1)操作的方式相同。
3、COUNT(某一列):
这个表达式统计的是指定列中非NULL值的数量。如果该列有NULL值,那么这些行不会被计入总数中。
区别
1、数据处理范围:
COUNT(*)
包括了所有行,无论列值是否为NULL;COUNT(1)
同样统计所有行,因为它计数的是恒定的1,不关心行数据的实际内容;而COUNT(某一列)
只统计该列非NULL值的行数。
2、性能影响因素:
由于COUNT(*)
和COUNT(1)
不需要访问实际的数据行(特别是当索引可以覆盖查询需求时),它们的执行效率可能高于COUNT(某一列)
,特别是当该列含有大量NULL值时。
3、应用场景:
如果你需要统计表的所有行数,包括那些具有NULL值的行,应使用COUNT(*)
或COUNT(1)
。若你需要统计特定列的非NULL值数量,则应使用COUNT(某一列)
。
性能比较
1、在MySQL中,COUNT(*)
和COUNT(1)
的性能差异微乎其微,甚至可以认为是等价的。在MySQL官网中,明确指出InnoDB引擎中两语句性能一致[1]。原文如下:
InnoDB handles SELECT COUNT(*) and SELECT COUNT(1) operations in the same way. There is no performance difference.
2、相比之下,COUNT(某一列)
的性能取决于该列的数据分布,特别是NULL值的比例。如果该列包含大量NULL值,且查询优化器无法有效利用索引,那么它的执行效率可能会低于前两者。其中COUNT(主键)相比COUNT(普通列)的性能要好。
3、实际性能还受到数据库索引、表结构、数据量、以及数据库管理系统具体实现等因素的影响。因此,在特定场景下,需进行实际测试。
结语
总的来说,COUNT(*)
和COUNT(1)
在统计全表行数时非常相似,且在多数情况下性能相当,推荐在不需要考虑特定列的NULL值时使用。而COUNT(某一列)
则适用于需要精确统计某列非NULL值数量的场景。在考虑性能优化时,了解并利用数据库的特定优化特性和执行计划分析工具,可以帮助做出更合适的选择。