面试官:请说一下Mysql中count(1)、count(*)以及count(列)的区别?

近期在Review项目代码时,发现同事们在查询MySQL行数时存在多样的方式,有的使用COUNT(1), 有的用COUNT(id), 还有人选择了COUNT(*)。这混杂的选择引发了我的思考。当然这三种count的方式也是众说纷纭,其中最大的分歧点就是COUNT(*)COUNT(1)查询性能上,有人觉得COUNT(*)需要转换为COUNT(1),所以COUNT(1)得速度更快。究竟这三种计数方式之间有何区别,它们的背后原理是怎样的呢?

COUNT()含义

在《高性能Mysql》一书第236页中是这么解释COUNT的作用的:

COUNT()是一个特殊的函数,有两种非常不同的作用:它可以统计某个列值的数量,也可以统计行数。在统计列值时要求列值是非空的(不统计NULL)。如果在COUNT()的括号中指定了列或者列的表达式,则统计的就是这个表达式有值的结果数”。因为很多人对 NULL理解有问题,所以这里很容易产生误解。
COUNT()的另一个作用是统计结果集的行数。当MySOL确认括号内的表达式值不可能为空时,实际上就是在统计行数。最简单的就是当我们使用COUNT()的时候,这种情况下通配符并不会像我们猜想的那样扩展成所有的列,实际上,它会忽略所有的列而直接统计所有的行数。
我们发现一个最常见的错误就是,在括号内指定了一个列却希望统计结果集的行数。如果希望知道的是结果集的行数,最好使用COUNT(*),这样写意义清晰,性能也会很好

由此我们也可以大概总结COUNT函数的种方式如下:

  • COUNT(1): 此查询返回的是结果集中的行数,不关心具体的列内容,因此使用常数1。
    在很多数据库系统中,这种方式被优化为与 SELECT COUNT(*) 相同的性能水平,因为数据库引擎通常忽略括号内的内容。

  • COUNT(*):统计整个表的行数,不考虑是否有NULL值。
    通常优于 COUNT(id),因为它不需要关心具体的列,且现代数据库引擎会对其进行特殊优化。

  • COUNT(列) :统计指定列非空值的数量。需要考虑是否有NULL值
    此种方式取决于列是否有索引。如果 列有索引,数据库引擎可能会利用索引进行快速计数。如果没有索引,或者有大量NULL值,性能可能较差,因为需要扫描整个表。

区别

1、Mysql5.7

在MySql 5.7官方文档中是这么介绍COUNT(expr)函数的

COUNT(expr)
Returns a count of the number of non-NULL values of expr in the rows retrieved by a SELECT statement. The result is a BIGINT value.
If there are no matching rows, COUNT() returns 0.
mysql> SELECT student.student_name,COUNT(*)
FROM student,course
WHERE student.student_id=course.student_id
GROUP BY student_name;
COUNT(*) is somewhat different in that it returns a count of the number of rows retrieved, whether or not they contain NULL values.

For transactional storage engines such as InnoDB, storing an exact row count is problematic. Multiple transactions may be occurring at the same time, each of which may affect the count.

InnoDB does not keep an internal count of rows in a table because concurrent transactions might “see” different numbers of rows at the same time. Consequently, SELECT COUNT(*) statements only count rows visible to the current transaction.

Prior to MySQL 5.7.18, InnoDB processes SELECT COUNT(*) statements by scanning the clustered index. As of MySQL 5.7.18, InnoDB processes SELECT COUNT(*) statements by traversing the smallest available secondary index unless an index or optimizer hint directs the optimizer to use a different index. If a secondary index is not present, the clustered index is scanned.

Processing SELECT COUNT(*) statements takes some time if index records are not entirely in the buffer pool. For a faster count, create a counter table and let your application update it according to the inserts and deletes it does. However, this method may not scale well in situations where thousands of concurrent transactions are initiating updates to the same counter table. If an approximate row count is sufficient, use SHOW TABLE STATUS.

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. For example:
mysql> SELECT COUNT(*) FROM student;
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.

从官方文档中我们可以看出mysql官方对COUNT函数的解释:

  • COUNT(expr) 返回由 SELECT 语句检索的行中 expr 的非 NULL 值的数量,结果为 BIGINT 值。如果没有匹配的行,COUNT() 返回 0。

  • COUNT(*) 有所不同,它返回所检索的行数的计数,无论它们是否包含 NULL 值。

  • 对于事务性存储引擎(如 InnoDB),存储准确的行数是有问题的。因为多个事务可能同时影响计数,InnoDB 不会保留表中行的内部计数。SELECT COUNT(*) 只会计算当前事务可见的行。

  • 在 MySQL 5.7.18 之前,InnoDB 通过扫描聚集索引处理 SELECT COUNT(*) 语句。从 MySQL 5.7.18 开始,除非索引或优化器提示指示使用其他索引,InnoDB 会通过遍历最小的可用二级索引来处理 SELECT COUNT(*) 语句。如果没有二级索引,则将扫描聚集索引。

  • 处理 SELECT COUNT(*) 语句可能花费一些时间,如果索引记录没有完全在缓冲池中。为了更快的计数,可以创建一个计数器表,并根据插入和删除操作进行更新。然而,在成千上万的并发事务更新同一计数器表的情况下,该方法可能无法很好地扩展。如果粗略的行数足够,可以使用SHOW TABLE STATUS

  • InnoDB 处理 SELECT COUNT(*)SELECT COUNT(1) 操作的方式相同,没有性能差异。

  • 对于 MyISAM 表,COUNT(\*) 在从一个表中检索、没有检索其他列、没有 WHERE 子句的情况下可以快速返回,因为 MyISAM 存储了准确的行数。COUNT(1) 只有在第一列被定义为 NOT NULL 时才能进行相同的优化。

2、Mysql 8.0

在Mysql8.0的文档中对COUNT(expr)的解释是这样

COUNT(expr) [over_clause]

Returns a count of the number of non-NULL values of expr in the rows retrieved by a SELECT statement. The result is a BIGINT value.

If there are no matching rows, COUNT() returns 0.COUNT(NULL)returns 0.

This function executes as a window function if over_clause is present. over_clause is as described in Section 12.20.2, “Window Function Concepts and Syntax”.

mysql> SELECT student.student_name,COUNT(*) FROM student,course WHERE student.student_id=course.student_id GROUP BY student_name;
COUNT(*) is somewhat different in that it returns a count of the number of rows retrieved, whether or not they contain NULL values.

For transactional storage engines such as InnoDB, storing an exact row count is problematic. Multiple transactions may be occurring at the same time, each of which may affect the count.

InnoDB does not keep an internal count of rows in a table because concurrent transactions might “see” different numbers of rows at the same time. Consequently, SELECT COUNT(*) statements only count rows visible to the current transaction.

As of MySQL 8.0.13, SELECT COUNT(*) FROM tbl_name query performance for InnoDB tables is optimized for single-threaded workloads if there are no extra clauses such as WHERE or GROUP BY.

InnoDB processes SELECT COUNT(*) statements by traversing the smallest available secondary index unless an index or optimizer hint directs the optimizer to use a different index. If a secondary index is not present, InnoDB processes SELECT COUNT(*) statements by scanning the clustered index.

Processing SELECT COUNT(*) statements takes some time if index records are not entirely in the buffer pool. For a faster count, create a counter table and let your application update it according to the inserts and deletes it does. However, this method may not scale well in situations where thousands of concurrent transactions are initiating updates to the same counter table. If an approximate row count is sufficient, use SHOW TABLE STATUS.

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. For example:

mysql> SELECT COUNT(*) FROM student;
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.

从mysql8.0的文档中我们可以看出mysql8.0对COUNT的解释

  • COUNT(expr) 返回在由 SELECT 语句检索的行中 _expr_ 的非 NULL 值的数量,结果为 BIGINT 值。如果没有匹配的行,COUNT() 返回 0。COUNT(NULL) 也返回 0。

  • COUNT(*) 有所不同,它返回所检索的行数的计数,无论它们是否包含 NULL 值。

  • 对于事务性存储引擎(如 InnoDB),存储准确的行数是有问题的,因为多个事务可能同时影响计数。InnoDB 不会保留表中行的内部计数。SELECT COUNT(*) 只计算当前事务可见的行。

  • 在 MySQL 8.0.13 及以后版本,对于 InnoDB 表,执行 SELECT COUNT(*) FROM tbl_name 查询性能在没有额外子句(如 WHERE 或 GROUP BY)的情况下进行了优化,特别适用于单线程工作负载。

  • InnoDB 处理 SELECT COUNT(*) 语句的方式:

    • 通过遍历最小可用二级索引,除非指示使用其他索引。
    • 如果没有二级索引,InnoDB 通过扫描聚集索引来处理 SELECT COUNT(*) 语句。
  • 处理 SELECT COUNT(*) 语句可能花费一些时间,如果索引记录没有完全在缓冲池中。为了更快的计数,可以创建一个计数器表,让应用程序根据插入和删除操作进行更新。但是,这种方法在数千个并发事务同时对同一计数器表进行更新的情况下可能不会很好地扩展。如果粗略的行数足够,可以使用 SHOW TABLE STATUS

  • InnoDB 对待 SELECT COUNT(*)SELECT COUNT(1) 操作的方式相同,没有性能差异。

  • 对于 MyISAM 表,COUNT(*) 在从一个表中检索、没有检索其他列、没有 WHERE 子句的情况下可以快速返回,因为 MyISAM 存储了准确的行数。COUNT(1) 只有在第一列被定义为 NOT NULL 时才能进行相同的优化。

结合Mysql5.7与Mysql8.0的文档我们可以看出两个版本对COUNT的支持的差异:

  • MySQL 8.0 优化了 InnoDB 表的 SELECT COUNT(*) 查询性能: 在 MySQL 8.0.13 及以后版本,对于 InnoDB 表,执行 SELECT COUNT(*) 查询的性能进行了优化,特别适用于单线程工作负载。这是 MySQL 5.7 文档中未包含的新特性。
  • MyISAM 表的优化说明:
    MySQL 8.0 文档中强调了 MyISAM 表在执行 COUNT(*) 时的优化情况,即在从一个表中检索、没有检索其他列、没有 WHERE 子句的情况下可以快速返回。MySQL 5.7 文档中也提到了 MyISAM 表的优化,但对于 COUNT(*) 的具体优化情况没有细节。

MySQL 8.0 在性能优化方面对于 InnoDB 表的 SELECT COUNT(*) 查询进行了特别的关注,而且在 MyISAM 表的优化方面进行了详细的说明。其他方面,两个版本在COUNT()函数的解释和使用上基本保持一致。

基于此我们明白,其实COUNT(*)COUNT(1)其实是一样的,在性能上并没有差异。

那这两种方式与COUNT(列)的差异呢?从以上《高性能Mysql》以及Mysql官方文档中我们知道,COUNT(列)是统计非空列的行数,它也会遍历整张表,然后会对列对应的值做非空判断,非空的字段进行个数累加。当然这是列为主键索引时的操作。如果列不为主键索引时,那么查询时还需要进行回表操作,再根据主键获取数据,此时无疑是增加了一次IO,在性能上其实是不如COUNT(*)COUNT(1)的。那么我们就可以知道,按照效率来看,count(*) = count(1) > count(主键) > count(非主键列)

使用建议

  • 如果你想知道一张表的大概行数,我们可以直接使用show table status命令或者咱们使用的一些mysql客户端Navicat或者datagrip都可以办到。

  • 如果你想获取一张表的确切行数时,我可以是优先使用使用 COUNT(*) 获取行数,这样写法清晰,性能较好,尤其对于 InnoDB 表的优化更为明显。我们要避免使用 COUNT(列) 统计行数,除非你真的需要统计该列非空值的数量,否则容易产生误解。

篇幅有限,深入验证将在后续文章中介绍。

本文已收录于我的个人博客:码农Academy的博客,专注分享Java技术干货,包括Java基础、Spring Boot、Spring Cloud、Mysql、Redis、Elasticsearch、中间件、架构设计、面试题、程序员攻略等

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/612682.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据库mysql no.3

1.排序查询 order by 排序列表 【asc/desc】 排序列表:可以是单个字段、多个字段、表达式、函数、别名。 asc 升序 desc 降序 如果没有写那就是默认升序 2.常见函数 select 函数名(); 定义:函…

创建mysql普通用户

一、创建mysql普通用户的原因: 权限控制:MySQL的权限系统允许您为每个用户分配特定的权限。通过创建普通用户,您可以根据需要为每个用户分配特定的数据库和表权限,而不是将所有权限授予一个全局管理员用户。这有助于提高数据库的…

[算法与数据结构][c++]:Static关键字和全局变量

Static关键字和全局变量 1. 生命周期、作用域和初始化时机2. 全局变量3. Static 关键字3.1 面向过程3.1.1 静态全局变量3.1.2 静态局部变量(单例中会使用)3.1.3 静态函数 3.2 面向对象3.2.1 类内静态成员变量3.2.2 类内静态成员函数 Reference 写在前面&…

Taro+vue3 实现选座位 功能 以及座位显示

1.类似选座位那种功能 我的功能座位 不是html元素 而是 座位图片 都是图片 const onConfirm () > {// const area_arr selectedSeat.value.map((item) > {// return item.areaId;// });// const abc isRepeat(area_arr);// if (!abc) {// Taro.showToast({//…

水经微图安卓版APP正式上线!

在水经微图APP(简称“微图APP”)安卓版已正式上线! 在随着IOS版上线约一周之后,安卓版终于紧随其后发布了。 微图安卓版APP下载安装 自从IOS版发布之后,就有用户一直在问安卓版什么时候发布,这里非常感谢…

【复盘】quartz job 停止调度原因调查解决

场景 项目中的定时任务由 quartz 调度,划分了多个模块。测试组发现了其中A模块的定时任务不执行了,这就让人很头疼。 排查 1、job 不执行的原因有可能是 quartz 线程池满导致的问题 2、代码中未正确配置,上一个job 未执行完成下一个job 继…

Windows下安装mariadb10.5数据库及配置详细教程

1、简介 MariaDB数据库管理系统是一款MySQL的替代数据库。MariaDB由MySQL的创始人麦克尔维德纽斯主导开发,是可扩展的,可靠的SQL服务器的合乎逻辑的选择,MariaDB 10.5 是 MariaDB 当前的稳定系列。 2、下载 下载地址:Download M…

Linux和windows进程同步与线程同步那些事儿(五):Linux下进程同步

Linux和windows进程同步与线程同步那些事儿(一) Linux和windows进程同步与线程同步那些事儿(二): windows线程同步详解示例 Linux和windows进程同步与线程同步那些事儿(三): Linux线…

Linux基础知识(文件类型、目录、文件权限、权限修改)

Linux基础知识(文件类型、目录、文件权限、权限修改) 文章目录 Linux基础知识(文件类型、目录、文件权限、权限修改)0x01 文件类型0x02 常用目录0x03 文件系统权限0x04 权限修改 0x01 文件类型 Linux中的文件分类主要基于其内容和…

基于ssm快餐店点餐结算系统的设计与实现+vue论文

摘 要 传统办法管理信息首先需要花费的时间比较多,其次数据出错率比较高,而且对错误的数据进行更改也比较困难,最后,检索数据费事费力。因此,在计算机上安装快餐店点餐结算系统软件来发挥其高效地信息处理的作用&…

前端计算精度丢失问题

// 精度丢失异常数据汇总 10.3950 * 3935.00 期望40904.33, 异常结果40904.32 7.3950 * 3835.00 期望28359.83, 异常结果28359.82 11.777 * 4215 期望49640.06, 异常结果49640.05 12.445 * 4005 期望49842.23,异常结果49842.2…

区间预测 | Matlab实现CNN-LSTM-KDE的卷积长短期神经网络结合核密度估计多变量时序区间预测

区间预测 | Matlab实现CNN-LSTM-KDE的卷积长短期神经网络结合核密度估计多变量时序区间预测 目录 区间预测 | Matlab实现CNN-LSTM-KDE的卷积长短期神经网络结合核密度估计多变量时序区间预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.CNN-LSTM-KDE多变量时间序列区…

使用 Windbg 分析软件异常时的诸多细节与技巧总结

目录 1、dump文件 1.1、dump文件的生成方式 1.2、dump文件的大小 2、pdb符号文件 2.1、pdb文件的路径设置 2.2、pdb文件的时间戳与名称问题 2.3、如何确定要找哪些pdb文件? 3、使用Windbg静态分析dump文件以及动态调试程序的一般步骤 4、确定发生异常或崩溃…

能力素质模型在企业中的应用

在我国经济飞速发展中,企业也获得了越来越多的业务。业务的增加带来的是人力管理上的不足,很多企业一味的着眼于业务的营收,而忽视了人力资源管理上的跟进,人力资源管理水平逐渐跟不上业务的迅猛发展,所以企业需要一套…

程序员的海外营收

在素材图片平台生成AI图片,通过下载赚取版税。 Wirestock这个平台能让你方便地把作品上传到多个素材市场,比如Adobe Stock、Shutterstock和iStock等。 你只需要上传一次,就能在多个平台上销售。 你可以开作坊、网上教程,或者在…

使用Netty实现Socket网络编程

** Netty初步讲解和认识 ** 网络通信模型 Netty支持多种网络通信模型,包括传统的阻塞I/O、非阻塞I/O、多路复用I/O和异步I/O。其中,非阻塞I/O和多路复用I/O是Netty的核心特性。 非阻塞I/O:Netty通过使用Java的NIO(New I/O&…

JavaScript删除数组中指定元素的五种方法有哪些

JavaScript中删除数组中指定元素的方法有多种,以下列出五种常见方法: 使用filter()方法:filter()方法创建一个新数组,新数组中的元素是通过检查指定条件后从原数组中筛选出来的。如果元素满足条件,它就会被包含在新数组…

buuctf[极客大挑战 2019]BabySQL--联合注入、双写过滤

目录 1、测试万能密码: 2、判断字段个数 3、尝试联合注入 4、尝试双写过滤 5、继续尝试列数 6、查询数据库和版本信息 7、查询表名 8、没有找到和ctf相关的内容,查找其他的数据库 9、查看ctf数据库中的表 10、查询Flag表中的字段名 11、查询表…

Python图像处理【17】指纹增强和细节提取

指纹增强和细节提取 0. 前言1. 形态学操作基础2. 利用形态学操作进行指纹增强3. 从增强指纹中提取特征(细节)3.1 指纹细节概念3.2 提取指纹细节 小结系列链接 0. 前言 指纹识别和验证是最古老,最流行和广泛使用的生物特征技术。众所周知,每个人都有独特…

模型\视图一般步骤:为什么经常要用“选择模型”QItemSelectionModel?

一、“使用视图”一般的步骤: //1.创建 模型(这里是数据模型!) tabModelnew QSqlTableModel(this,DB);//数据表 //2.设置 视图的模型(这里是数据模型!) ui->tableView->setModel(tabModel); 模型种类: QStringListModel…