MySql 执行countcount(*) 与 count(列名) 区别
Posted I believe I can fly~
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySql 执行countcount(*) 与 count(列名) 区别相关的知识,希望对你有一定的参考价值。
mysql 执行count(1)、count(*) 与 count(列名) 区别
1. 初识 count
-
COUNT(expr) ,返回 SELECT 语句检索的行中 expr 的值不为NULL的数量。结果是一个 BIGINT 值。
-
如果查询结果没有命中任何记录,则返回 0。
-
COUNT(*) 的统计结果中,会包含值为 NULL 的行数。
除了 COUNT(id) 和 COUNT(*) 以外,还可以使用 COUNT(常量)(如 COUNT(1) )来统计行数。
2. COUNT(列名)、COUNT(1)和COUNT(*)之间的区别
执行效果上:
- count(*) 包括了所有的列,相当于行数,在统计结果的时候,不会忽略列值为 NULL 。
- count(1) 包括了忽略所有列(不专门统计哪一列,只要有数据,就代表一行),用1代表代码行,在统计结果的时候,不会忽略列值为 NULL 。
- count(列名) 只包括列名那一列,在统计结果的时候,会忽略列值为空(这里的空不是只空字符串或者 0,而是表示 null)的计数,即某个字段值为 NULL 时,不统计。
执行效率上:
count(主键) count(*) count(1) 效率远高于 count(非主键列)
- count(*) count(1), count(列,主键) 执行计划基本上是一样的。
- count(列名(非主键)) 的执行计划 type = All 是进行的全表扫描,而 count(*) count(1), count(列,主键) 的 type 是 null,执行时甚至不用访问表。
MySQL5.7 文档中有一段话:
InnoDB handles SELECT COUNT(*) and SELECT COUNT(1) operations in the same way. There is no performance difference.
For MyISAM tables, COUNT(*) is optimized to return very quickly if the SELECT retrieves from one table, no other columns are retrieved, and there is no WHERE clause.This optimization only applies to MyISAM tables, because an exact row count is stored for this storage engine and can be accessed very quickly. COUNT(1) is only subject to the same optimization if the first column is defined as NOT NULL.
InnoDB 以同样的方式处理 SELECT COUNT(*) 和 SELECT COUNT(1) 操作。两者没有性能差异。
对于 MyISAM 表,如果 SELECT 从一个表中检索,没有检索其他列,也没有 WHERE 子句,那么 COUNT(*) 被优化为快速返回。这种优化只适用于 MyISAM 表,因为这个存储引擎存储了准确的行数,并且可以非常快速地访问。COUNT(1) 只有在第一列被定义为 NOT NULL 时才进行与 COUNT(*) 相同的优化。
3. MyISAM
MyISAM 在统计表的总行数的时候会很快,但是有个大前提,不能加有任何 WHERE 条件。这是因为:MyISAM 对于表的行数做了优化,具体做法是有一个变量存储了表的行数,如果查询条件没有 WHERE 条件则是查询表中一共有多少条数据,MyISAM 可以做到迅速返回,所以也解释了如果加 WHERE 条件,则该优化就不起作用了。细心的同学会发现,innodb 的表也有这么一个存储了表行数的变量,但是很遗憾这个值是一个估计值,没有什么实际意义。
4. Innodb
COUNT() 函数的具体含义:
COUNT() 有两个非常不同的作用:
- 它可以统计某个列值的数量,也可以统计行数。
- 在统计列值时要求列值是非空的(不统计 NULL)。如果在 COUNT(expr) 的括号中定了列或者列表达式,则统计的就是这个表达式有值的结果数。
- COUNT() 的另一个作用是统计结果集的行数。当 MySQL 确认括号内的表达式值不可能为空时,实际上就是在统计行数。最简单的就是当我们使用 COUNT(*) 的时候,这种情况下通配符 ***** 并不像我们猜想的那样扩展成所有的列,实际上,他会忽略所有列而直接统计所有的行数“——《高性能MySQL》。
Innodb COUNT 用法:
如果该表只有一个主键索引,没有任何二级索引的情况下,那么 COUNT(*) 和 COUNT(1) 都是通过通过主键索引来统计行数的。如果该表有二级索引,则COUNT(1) 和 COUNT(*) 都会通过占用空间最小的字段的二级索引进行统计。
原理
目前基于磁盘的数据库或者搜索引擎(比如 Lucene)的性能瓶颈主要都是在 IO 阶段,相比于 CPU 和 RAM,IO 操作实在太慢了,所以这类系统的优化方向也都都是类似的——尽一切可能减少 IO 的次数(所以很多用 ES 的程序在性能优化到极限的时候选择直接上 SSD)。这里统计行数的操作,查询优化器的优化方向就是选择能够让 IO 次数最少的索引,也就是基于占用空间最小的字段所建的索引(每次 IO 读取的数据量是固定的,索引占用的空间越小所需的 IO 次数也就越少)。而 Innodb 的主键索引是聚簇索引(包含了 KEY,除了 KEY 之外的其他字段值,事务 ID 和 MVCC 回滚指针)所以主键索引一定会比二级索引(包含 KEY 和对应的主键 ID)大,也就是说在有二级索引的情况下,一般 COUNT() 都不会通过主键索引来统计行数,在有多个二级索引的情况下选择占用空间最小的。
如果说有张 Innodb 的表只有主键索引,而且记录还比较大(比如30K),则统计行的操作会非常慢,因为 IO 次数会很多(这里就不做实验截图了,有兴趣可以自己试一下)。一个优化方案就是预先建一个小字段并建二级索引专门用来统计行数,极端情况下这种优化速度提高上千倍也是正常的。
结论
-
COUNT(1) 和 COUNT(*) 执行优化器的优化是完全一样的,并没有 COUNT(1) 会比 COUNT(*) 快这个说法。
-
COUNT(列名) 需要进行字段的非 NULL 判断,所以效率会低一些。
当然遇到有些情况优化器的选择也不一定总是最优的,如果你坚持要用索引,可以通过 FORCE INDEX 来强制指定你要使用的索引。
参考:
MySQL学习笔记:count(1)、count(*)、count(字段)的区别 - Hider1214 - 博客园 (cnblogs.com)
MYSQL 下 count(*)、count(列)、 count(1) 理解 - 腾讯云开发者社区-腾讯云 (tencent.com)
高性能MySQL——Count(1) OR Count(*)? - 知乎 (zhihu.com)
以上是关于MySql 执行countcount(*) 与 count(列名) 区别的主要内容,如果未能解决你的问题,请参考以下文章
执行countcount(*) 与 count(列名) 到底有什么区别?
执行countcount(*) 与 count(列名) 到底有什么区别?
select count(*)countcount的区别和执行效率比较