MySQL 中 count(*)、count(1) 和 count(字段名) 有什么区别?

March 15, 2025 / 十六 / 9阅读 / 0评论/ 分类: 数据库

重点回答

count(*)count(1)count(字段名)都是用来统计行数的聚合函数。

写法

含义

是否包含NULL行

性能

count(*)

统计所有行

包含

最优,MySQL会对count(*)进行优化

count(1)

统计所有行(常量代替*)

包含

等价于count(*)

count(字段名)

统计该字段中非NULL值的行

不包含

需要检查字段值是否为NULL,性能略低

InnoDB是通过B+tree来保存数据记录的,根据索引类型分为聚簇索引(主键索引)、非聚簇索引(二级索引),区别主要在于:聚簇索引的叶子节点存放的是实际数据,而二级索引叶子节点存放的是主键值,不是实际数据。

1)count(主键字段)

如果表中只有主键索引,没有二级索引,那InnoDB会循环遍历主键索引,将读取到的记录返回给server层,然后读取记录中的主键值,不为空,则将count变量+1;

但是,如果表中存在二级索引,那循环遍历对象就是二级索引,因为相同数量的二级索引记录比聚簇索引占用的空间更小,所以二级索引树比聚簇索引树小,这样一来,遍历二级索引的I/O成本就更低。因此,查询优化器会优先选择二级索引。

2)count(*)/count(1)

统计所有行,只计算行数,不做任何处理。

count(*)count(1)效率一致,虽然网上有很多说法,不过官网还是进行了说明:

https://dev.mysql.com/doc/refman/8.4/en/aggregate-functions.html

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

InnoDB 处理 SELECT COUNT(*) 和 SELECT COUNT(1) 操作的方式相同。两者在性能上没有区别。

并且MySQL会对count(*)count(1)进行优化,当有多个二级索引时,优化器优先选择key_len最小的索引进行扫描;没有二级索引,才会采用主键索引来统计。

3)count(字段名)

没有走索引,采用全表扫描的方式来计数,并且需要判断字段值是否为NULL,查询效率较低。

小结:

count(*)count(1)count(主键字段)在执行时,如果存在二级索引,查询优化器会选择二级索引(key_len最小的)进行扫描。

非必须不使用count(字段名)来统计行数,效率相对较低,会采用全表扫描的方式进行统计;如果有必须查询不为NULL的记录数需要,建议给该字段建立二级索引。

扩展

在MyISAM存储引擎中,由于只有表锁,因此会将每张表的总数进行单独记录维护(表锁使得对表的修改是串行,因此可以维护总数),所以count(*)非常快,相当于直接返回一个字段。前提是不需要条件过滤,而是直接返回整表数据

相对比下,InnoDB支持行锁,会有很多并发来修改表的数据,所以无法维护记录总数,但是InnoDB也对count(*)count(1)做了一定优化。当有多个二级索引时,优化器优先选择key_len最小的索引进行扫描;没有二级索引,才会采用主键索引来统计。前提也是没有对应的过滤条件。

#MySQL(21)

评论