MySQL的count(*) ,count(1),count(id)的区别

2023-10-27

数据库查询相信很多人都不陌生,所有经常有人调侃程序员就是CRUD专员,这所谓的CRUD指的就是数据库的增删改查。

在数据库的增删改查操作中,使用最频繁的就是查询操作。而在所有查询操作中,统计数量操作更是经常被用到。

关于数据库中行数统计,无论是MySQL还是Oracle,都有一个函数可以使用,那就是COUNT。

但是,就是这个常用的COUNT函数,却暗藏着很多玄机,尤其是在面试的时候,一不小心就会被虐。不信的话请尝试回答下以下问题:
 

 
  1. COUNT有几种用法?

  2. COUNT(字段名)和COUNT(*)的查询结果有什么不同?

  3. COUNT(1)和COUNT(*)之间有什么不同?

  4. COUNT(1)和COUNT(*)之间的效率哪个更高?

  5. 为什么《阿里巴巴Java开发手册》建议使用COUNT(*)

  6. MySQL的MyISAM引擎对COUNT(*)做了哪些优化?

  7. MySQL的InnoDB引擎对COUNT(*)做了哪些优化?

  8. 上面提到的MySQL对COUNT(*)做的优化,有一个关键的前提是什么?

  9. SELECT COUNT(*) 的时候,加不加where条件有差别吗?

  10. COUNT(*)、COUNT(1)和COUNT(字段名)的执行过程是怎样的?

以上10道题,如果您可以全部准确无误的回答的话,那说明你真的很了解COUNT函数了,如果有哪些知识点是不了解的,那么本文正好可以帮你答疑解惑。

认识COUNT

关于COUNT函数,在MySQL官网中有详细介绍:

简单翻译一下:

1、COUNT(expr) ,返回SELECT语句检索的行中expr的值不为NULL的数量。结果是一个BIGINT值。

2、如果查询结果没有命中任何记录,则返回0

3、但是,值得注意的是,COUNT(*) 的统计结果中,会包含值为NULL的行数。

即以下表记录示例:

 
  1. create table #bla(id int,id2 int)

  2. insert #bla values(null,null)

  3. insert #bla values(1,null)

  4. insert #bla values(null,1)

  5. insert #bla values(1,null)

  6. insert #bla values(null,1)

  7. insert #bla values(1,null)

  8. insert #bla values(null,null)

使用语句count(*),count(id),count(id2)查询结果如下:

 
  1. select count(*),count(id),count(id2)

  2. from #bla

结果依次是: 7  3  2

除了COUNT(id)和COUNT(*)以外,还可以使用COUNT(常量)(如COUNT(1))来统计行数,那么这三条SQL语句有什么区别呢?到底哪种效率更高呢?为什么《阿里巴巴Java开发手册》中强制要求不让使用 COUNT(列名)或 COUNT(常量)来替代 COUNT(*)呢?

 


 

COUNT(列名)、COUNT(常量)和COUNT(*)之间的区别
 

前面我们提到过COUNT(expr)用于做行数统计,统计的是expr不为NULL的行数,那么COUNT(列名)、 COUNT(常量) 和 COUNT(*)这三种语法中,expr分别是列名、 常量 和 *

那么列名、 常量 和 *这三个条件中,常量 是一个固定值,肯定不为NULL。*可以理解为查询整行,所以肯定也不为NULL,那么就只有列名的查询结果有可能是NULL了。

所以, COUNT(常量) 和 COUNT(*)表示的是直接查询符合条件的数据库表的行数。而COUNT(列名)表示的是查询符合条件的列的值不为NULL的行数。

除了查询得到结果集有区别之外,COUNT(*)相比COUNT(常量) 和 COUNT(列名)来讲,COUNT(*)是SQL92定义的标准统计行数的语法,因为他是标准语法,所以MySQL数据库对他进行过很多优化。

SQL92,是数据库的一个ANSI/ISO标准。它定义了一种语言(SQL)以及数据库的行为(事务、隔离级别等)。

COUNT(*)的优化

前面提到了COUNT(*)是SQL92定义的标准统计行数的语法,所以MySQL数据库对他进行过很多优化。那么,具体都做过哪些事情呢?

这里的介绍要区分不同的执行引擎。MySQL中比较常用的执行引擎就是InnoDB和MyISAM。

MyISAM和InnoDB有很多区别,其中有一个关键的区别和我们接下来要介绍的COUNT(*)有关,那就是MyISAM不支持事务,MyISAM中的锁是表级锁;而InnoDB支持事务,并且支持行级锁。

因为MyISAM的锁是表级锁,所以同一张表上面的操作需要串行进行,所以,MyISAM做了一个简单的优化,那就是它可以把表的总行数单独记录下来,如果从一张表中使用COUNT(*)进行查询的时候,可以直接返回这个记录下来的数值就可以了,当然,前提是不能有where条件。

MyISAM之所以可以把表中的总行数记录下来供COUNT(*)查询使用,那是因为MyISAM数据库是表级锁,不会有并发的数据库行数修改,所以查询得到的行数是准确的。

但是,对于InnoDB来说,就不能做这种缓存操作了,因为InnoDB支持事务,其中大部分操作都是行级锁,所以可能表的行数可能会被并发修改,那么缓存记录下来的总行数就不准确了。

但是,InnoDB还是针对COUNT(*)语句做了些优化的。

在InnoDB中,使用COUNT(*)查询行数的时候,不可避免的要进行扫表了,那么,就可以在扫表过程中下功夫来优化效率了。

从MySQL 8.0.13开始,针对InnoDB的SELECT COUNT(*) FROM tbl_name语句,确实在扫表的过程中做了一些优化。前提是查询语句中不包含WHERE或GROUP BY等条件。

我们知道,COUNT(*)的目的只是为了统计总行数,所以,他根本不关心自己查到的具体值,所以,他如果能够在扫表的过程中,选择一个成本较低的索引进行的话,那就可以大大节省时间。

我们知道,InnoDB中索引分为聚簇索引(主键索引)和非聚簇索引(非主键索引),聚簇索引的叶子节点中保存的是整行记录,而非聚簇索引的叶子节点中保存的是该行记录的主键的值。

所以,相比之下,非聚簇索引要比聚簇索引小很多,所以MySQL会优先选择最小的非聚簇索引来扫表。所以,当我们建表的时候,除了主键索引以外,创建一个非主键索引还是有必要的。

至此,我们介绍完了MySQL数据库对于COUNT(*)的优化,这些优化的前提都是查询语句中不包含WHERE以及GROUP BY条件。

COUNT(*)和COUNT(1)

介绍完了COUNT(*),接下来看看COUNT(1),对于,这二者到底有没有区别,网上的说法众说纷纭。

有的说COUNT(*)执行时会转换成COUNT(1),所以COUNT(1)少了转换步骤,所以更快。

还有的说,因为MySQL针对COUNT(*)做了特殊优化,所以COUNT(*)更快。

那么,到底哪种说法是对的呢?看下MySQL官方文档是怎么说的:

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

画重点:same way , no performance difference所以,对于COUNT(1)和COUNT(*),MySQL的优化是完全一样的,根本不存在谁比谁快!

那既然COUNT(*)COUNT(1)一样,建议用哪个呢?

建议使用COUNT(*)!因为这个是SQL92定义的标准统计行数的语法,而且本文只是基于MySQL做了分析,关于Oracle中的这个问题,也是众说纷纭的呢。

COUNT(字段)

最后,就是我们一直还没提到的COUNT(字段),他的查询就比较简单粗暴了,就是进行全表扫描,然后判断指定字段的值是不是为NULL,不为NULL则累加。

相比COUNT(*)COUNT(字段)多了一个步骤就是判断所查询的字段是否为NULL,所以他的性能要比COUNT(*)慢。

总结

本文介绍了COUNT函数的用法,主要用于统计表行数。主要用法有COUNT(*)COUNT(字段)COUNT(1)

因为COUNT(*)是SQL92定义的标准统计行数的语法,所以MySQL对他进行了很多优化,MyISAM中会直接把表的总行数单独记录下来供COUNT(*)查询,而InnoDB则会在扫表的时候选择最小的索引来降低成本。当然,这些优化的前提都是没有进行where和group的条件查询。

在InnoDB中COUNT(*)COUNT(1)实现上没有区别,而且效率一样,但是COUNT(字段)需要进行字段的非NULL判断,所以效率会低一些。

因为COUNT(*)是SQL92定义的标准统计行数的语法,并且效率高,所以请直接使用COUNT(*)查询表的行数!

参考资料:
https://dev.mysql.com/doc/refman/8.0/en/group-by-functions.html#function_count
《极客时间——MySQL实战45讲》

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MySQL的count(*) ,count(1),count(id)的区别 的相关文章

随机推荐

  • jittor框架

    sudo apt install python dev sudo pip install https github com Jittor jittor git python3 m jittor test test example http
  • 概率算法与代码

    一 随机数 随机数在概率算法设计中扮演着十分重要的角色 在现实计算机上无法产生真正的随机数 因此在概率算法中使用的随机数都是一定程度上随机的 即伪随机数 线性同余法是产生伪随机数的最常用的方法 由线性同余法产生的随机序列a0 a1 an满足
  • es实战-rebalance功能及源码解析

    rebalance tasks在es集群里面的表现形式 通过调用 GET cat tasks v API 返回结果中 action 为 internal index shard recovery start recovery 不仅仅是reb
  • C#中Dispose、析构函数、close的区别

    C 中Dispose 析构函数 close的区别 一 Close与Dispose这两种方法的区别 调用完了对象的Close方法后 此对象有可能被重新进行使用 而Dispose方法来说 此对象所占有的资源需要被标记为无用了 也就是此对象要被销
  • c程序设计(第四版)、p102例4.6(查询成绩等级)

    谭浩强 c程序设计 第四版 p102例4 6 include
  • 抖音热门游戏Fill one-line puzzle game解法的Python代码实现

    抖音热门游戏Fill one line puzzle game解法的Python代码实现 导语 思路 代码 验证 谢谢小李同学的提醒 加上了无解的判断 不然如果无解就进入死循环了 版权声明 原创文章 转载请注明出处 导语 最近刷抖音的时候
  • c++综合练习题六

    1 t1 txt文件中保存某超市不同商品的销售情况 读取文件中的数据 要求使用容器存放 使用迭代器输出输出所有数据 对获取到的数据按照销售额进行降序排序并输出 对降序后的数据存入到新的文本t2 txt中 方法一 int main fstre
  • 无监督学习和自监督学习的区别

  • 解决windows系统wifi图标不见了的问题

    前言 最近大家在使用win11的时候是不是常常会遇到一个情况 那就是在某一次开机时wifi图标不见了 是不是很惊讶 没有动什么东西呀 难道wifi模块坏了 上次关机时挺正常的呀 是不是心里面很多疑问 哈哈 恰巧我最近也遇到了这个问题 刚开始
  • 检查IP或端口是否被封

    遇到IP连接超时 想要确认是否被封 可通过以下几个方式检验 IP可用性检测工具 Tools大全在线工具 Ping mtr dig and TCP port check from multiple locations 端口扫描 站长工具
  • Eclipse下,LogCat(deprecated),显示不了内容,或者全部都是Unknown

    这张图片中的下面的LogCat deprecated 显示不了内容 或者全部都是Unknown 我的解决方法是打开LogCat 没有 deprecated 标志的那个 deprecated 意思为弃用 废弃 不赞成 这样就能使用了 但是有些
  • 2023最新Python阅读书籍推荐

    a byte of python 中文 简明 Python 教程 入门的书很多 但能让新手轻松看懂的就少了 作者写的思路非常清晰 对每一个知识点讲解的很到位 不多不少 对初学者来说 力道刚刚好 这本书是Python入门最好的书 A byte
  • Eclipse IDE for C / C++ 安装教程 之 eclipse的安装及配置

    下载eclipse 前提 安装配置好JDK 官网下载eclipse installer eclipse下载器 或者官方下载对应版本zip 优点 方便省事 缺点 下载速度比较慢 安装器是64位的 对32位的机器不太友好 或者选择 清华大学开源
  • Vue Luckyexcel 将文件流转成file文件后使用Luckysheet回显数据 - 附完整示例

    Luckysheet 一款纯前端类似excel的在线表格 功能强大 配置简单 完全开源 官方文档 快速上手 Luckysheet文档快速上手 Luckysheet文档Luckysheet 一款纯前端类似excel的在线表格 功能强大 配置简
  • Basic Level 1075 链表元素分类 (25分)

    题目 给定一个单链表 请编写程序将链表元素进行分类排列 使得所有负值元素都排在非负值元素的前面 而 0 K 区间内的元素都排在大于 K 的元素前面 但每一类内部元素的顺序是不能改变的 例如 给定链表为 18 7 4 0 5 6 10 11
  • OpenCV常用方法总结

    图像导入 import cv2 as cv src cv imread lena jpg cv namedWindow input cv WINDOW AUTOSIZE cv imshow input src cv imwrite save
  • 大事件项目

    目录 1 0 资源地址 1 项目前期的准备工作 1 1 初始化项目结构 1 2 使用GitHub管理大事件的项目 1 3 安装VSCode的Live Server插件辅助开发 2 登录注册 2 1 绘制login页面的基本结构 2 2 实现
  • 【前端】使用ffmpeg+vue3实现简单的视频编辑器

    使用ffmpeg vue3实现简单的视频编辑器 主要依赖 ffmpeg ffmpeg vue 实现功能 视频转码 添加文字水印 添加图片水印 处理进度条和用时 文件编码信息 生成序列帧 界面效果 ffmpeg演示 渲染成品 fmpeg渲染成
  • linux命令之split

    splilt命令的目的是将文件按照一定的规则切分 实战 我现在有一份文件觉得太大 想要切分 split 1000000 a1 kongke company 1000000是按照每100w行去切分 a1 是后缀只有一个 文件名称默认就是xaa
  • MySQL的count(*) ,count(1),count(id)的区别

    数据库查询相信很多人都不陌生 所有经常有人调侃程序员就是CRUD专员 这所谓的CRUD指的就是数据库的增删改查 在数据库的增删改查操作中 使用最频繁的就是查询操作 而在所有查询操作中 统计数量操作更是经常被用到 关于数据库中行数统计 无论是