技术分享 | MySQL:count(*)、count(字段) 实现上区别
作者:高鹏
文章末尾有他著作的《深入理解 MySQL 主从原理 32 讲》 , 深入透彻理解 MySQL 主从 , GTID 相关技术知识 。
继上一篇文章:
MySQL:查询字段数量多少对查询效率的影响
我们继续来讨论一下 count(*)、count(字段)实现上的区别 。 注意我们这里都使用 Innodb 做为存储引擎 , 不讨论其他引擎 。 因为了有了前面的讨论 , 更容易看出它们的区别 , 这里我们有如下注意点:
- 我们需要做到执行计划一样 , 这里以全表扫描为例 。 实际上 count 很可能使用到覆盖索引(Using index) , 本文主要讨论它们实现的异同 。
- count(*) 和 count(字段) 在结果上可能并不一致 。 比如 count(字段) , 但是某些行本字段为 NULL 的话那么将不会统计行数 , 下面将会说明这种 NULL 判断的位置 。 本文还是使用简单的全表扫描来进行对比实现上的区别 。 首先我们要明确的是 count 使用的是一个 COUNT 计数器 。
mysql> show create tablebaguai_f\G*************************** 1. row ***************************Table: baguai_fCreate Table: CREATE TABLE `baguai_f` (`id` int(11) DEFAULT NULL,`a` varchar(20) DEFAULT NULL,`b` varchar(20) DEFAULT NULL,`c` varchar(20) DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf81 row in set (0.00 sec)mysql> select * from baguai_f;+------+------+------+------+| id| a| b| c|+------+------+------+------+|1 | g| g| NULL ||1 | g1| g1| g1||3 | g2| g2| g2||4 | g| g| NULL ||5 | g| g| NULL ||6 | g3| g3| g3|+------+------+------+------+6 rows in set (0.00 sec)mysql> desc select count(*) frombaguai_f where b='g';+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+| id | select_type | table| partitions | type | possible_keys | key| key_len | ref| rows | filtered | Extra|+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+|1 | SIMPLE| baguai_f | NULL| ALL| NULL| NULL | NULL| NULL |6 |16.67 | Using where |+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+1 row in set, 1 warning (0.00 sec)mysql> desc select count(c) frombaguai_f where b='g';+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+| id | select_type | table| partitions | type | possible_keys | key| key_len | ref| rows | filtered | Extra|+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+|1 | SIMPLE| baguai_f | NULL| ALL| NULL| NULL | NULL| NULL |6 |16.67 | Using where |+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+1 row in set, 1 warning (0.00 sec)mysql>select count(*) frombaguai_f where b='g';+----------+| count(*) |+----------+|3 |+----------+1 row in set (0.00 sec)mysql>select count(c) frombaguai_f where b='g';+----------+| count(c) |+----------+|0 |+----------+1 row in set (0.00 sec)这种不一致来自于 b='g' 的 c 列中都是 NULL 值 , 因此 count(c) 返回为 0 。二、示例中 count(*) 获取数据流程简析注意在《MySQL:查询字段数量多少对查询效率的影响》一文中我们已经详细的描述了部分流程 , 这里不再赘述 , 如果需要更加详细的了解 , 自行参考 。
1. MySQL 层 构建 read_set
这里构建的 read_set 实际上只会包含列b , 即一个字段 。
2. Innodb 层 构建模板
同理根据 read_set 构建的字段模板中只会包含列b 。
LOOP:这里开始循环返回每一条数据
3. Innodb 层 根据模板返回数据
这里我们可以看看模板的数量和模板对应的具体列名
- 模板的数量
断点:row_sel_store_mysql_rec查看模板数量:(gdb) p prebuilt->n_template$1 = 1- 查看模板对应的字段
断点:row_sel_field_store_in_mysql_format_func查看模板对应的字段:(gdb) p field->name$3 = {m_name = 0x7ffe7c99cf85 "b"}显然这里只是将 b 列的值返回给了 MySQL层 , 这里也很好理解 , 因为 b 列在 MySQL 层需要继续做过滤操作 。4. MySQL 层 过滤条件 b='g'
好了 , 当前返回给 MySQL 层的数据中只有 b 列的数据 , 然后施加 b='g' 这个条件进行过滤 。
【技术分享 | MySQL:count(*)、count(字段) 实现上区别】5. MySQL 层 过滤后做一个 COUNT 计数操作
对于普通的 select 语句过滤后的数据就可以返回了 , 但是对于 count 这种操作 , 这里做的是一个计数操作 , 其中行会对 count 字段的 NULL 值进行判断 , 当然这里是 count(*) 也就不存在 NULL 值判断了 , 下面是这段代码:
- 技术|做“视频”绿厂是专业的,这项技术获人民日报评论点赞
- 中国|浅谈5G移动通信技术的前世和今生
- 桌面|日常使用的软件及网站分享 篇一:几个动态壁纸软件和静态壁纸网站:助你美化你的桌面
- 速度|华为P50Pro或采用很吓人的拍照技术:液体镜头让对焦速度更快
- 视频社会生产力报告|视频社会雏形已成,绿厂或凭这技术抢占先机
- 职工组一等|全国人工智能应用技术技能大赛落幕 青岛四名选手获一等奖
- 中国视频|人日评论点赞!OPPO成视频手机先行者,新技术或下月发布
- 介绍|5分钟介绍各种类型的人工智能技术
- 重庆市工业互联网技术创新战略联盟:构建万物互联智能工厂 助力先进制造发展
- 热点功能|旧手机别乱处理,分享旧手机6个小妙用,放在家里好值钱
