关于 MySQL 的 count() 用法
count()函数概述
count() 是一个聚合函数,返回指定匹配条件的行数。开发中常用来统计表中的数据。
count(*) 的实现方式
MyISAM 引擎把一个表的总行数存在磁盘上,因此执行 count(*) 会直接返回这个数,效率很高。但是在加上 where 限定语句后 MySQL 需要对全表进行检索从而得出 count 的总数。
InnoDB 引擎执行 count(*) 时,需要把数据一行一行地从引擎里面读出来,然后累积计数。
为什么 InnoDB 不可以和 MyISAM 一样,把数字也存起来?
- 因为 InnoDB 即使在同一个时刻的多个查询,由于多版本兵法控制(MVCC)的原因,InnoDB 表返回行数也是不确定的。
InnoDB 在执行 count() 操作的时候做了优化
InnoDB 是索引组织表,主键索引树的叶子节点是数据,而普通索引树的叶子节点是主键值,所以普通索引树比主键索引树小很多。对于 count() 这样的操作,遍历哪个索引树得到的结果逻辑上都是一样的,因此 MySQL 优化器会找到最小的那棵树来遍历。在保证逻辑正确的前提下,尽量减少扫描的数据量,是数据库系统设计的通用法则之一。
show table status
命令输出结果有TABLE_ROWS
用于显示这个表当前有多少行,但不可代替 count(*),因为索引统计的值是通过采样来估算的(MySQL会索引选择错误导致)。
小结
MyISAM 表虽然 count(*) 很快,但是不支持事务;
show table status
命令虽然返回很快,但是不准确;InnoDB 表直接 count(*) 会遍历全表,虽然结果准确,但会导致性能的问题。
不同的 count 用法
count(主键id)
InnoDB 引擎会遍历整张表,把每一行的 id 值都取出来返回给 server 层。server 层拿到 id 后,判断不可能为空的就按行累加。
count(1)
InnoDB 引擎遍历整张表,但不取值。server 层对于返回的每一行,放一个数字“1”进去,判断是不可能为空的,按行累加。
count(1) 执行速度比 count(主键id) 快,因为从引擎返回 id 会涉及到解析数据行,以及拷贝字段值的操作。
count(字段)
如果字段是 not null 的话,一行行地从记录里面读出这个字段,判断不能为 null,按行累加;
如果这个字段允许为 null,那么执行的时候,还要取值判断是否为 null,不为 null 才累加。
count(*)
count() 是例外,不会把全部字段取出来,专门做了优化,不取值。count() 肯定不是 null,按行累加。
各个 count 用法的执行速度
count(字段)< count(主键 id) < count(1) ≈ count(*)
建议使用 count(*)
- 本文链接: https://lemonlyue.github.io/2023/10/03/关于MySQL的count/
- 版权声明: 本博客所有文章除特别声明外,均默认采用 CC BY-NC-SA 4.0 许可协议。