【MySQL统计函数count详解】
MySQL统计函数count详解1. count()概述2. count(1)和count(*)和count(列名)的区别3. count(*)的实现方式1. count()概述count() 是一个聚合函数返回指定匹配条件的行数。开发中常用来统计表中数据全部数据不为null数据或者去重数据。2. count(1)和count(*)和count(列名)的区别1.函数说明count(1)统计所有的记录包括null。count(*)统计所有的记录包括null。count(字段)统计该字段不为null的记录。count(distinct 字段)统计该字段去重且不为null的记录。count(1)中的1并不是表示第一个字段而是表示一个固定值。其实就可以想成表中有这么一个字段这个字段就是固定值1count(1)就是计算一共有多少个1。count(*)执行时会把星号翻译成字段的具体名字效果也是一样的不过多了一个翻译的动作比固定值的方式效率稍微低一些。2.执行效率他们之间根据不同情况会有些许区别MySQL 会对count()做优化。(1)如果表中只有一列则count( )效率最优。(2)如果表有多列且存在主键count (主键列名)效率最优其次是count (1) count( *)。(3)如果表有多列且不存在主键则count(1 )效率优于count( *)3.执行过程count(*)包括了所有的列相当于行数在统计结果的时候 包括列值为NULL的行。count(1)包括了忽略所有列用1代表代码行在统计结果的时候 包括列值为NULL的行。count(列名)只包括列名那一列在统计结果的时候会忽略列值为空这里的空不是只空字符串或者0而是表示null的计数 即某个字段值为NULL时不统计。4.注意事项阿里开发手册规范相关规定1.【强制】不要使用 count(列名)或 count(常量)来替代 count(),count()是 SQL92 定义的标 准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关. 说明count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行.2.【强制】count(distinct col) 计算该列除 NULL 之外的不重复行数,注意 count(distinct col1, col2) 如果其中一列全为 NULL,那么即使另一列有不同的值,也返回为 0.3. count(*)的实现方式在日常开发中经常会统计一个表的行数通过select count(*) from t很容易实现可是随着记录数越来越多统计函数执行越来越慢然后呢可能会想mysql记录个总数不行吗为什么每次都要逐行累加呢count(列名)的时候还要判空那么今天我们就聊聊count(*)的实现方式。你首先要明确的是在不同的MySQL引擎中count(*)有不同的实现方式。MyISAM引擎把一个表的总行数存在了磁盘上因此执行count()的时候会直接返回这个数效率很高而InnoDB引擎就麻烦了它执行count()的时候需要把数据一行一行地从引擎里面读出来然后累积计数。这里需要注意的是我们在这篇文章里讨论的是没有过滤条件的count(*)如果加了where 条件的话MyISAM表也是不能返回得这么快的。我们一起分析了为什么要使用InnoDB因为不论是在事务支持、并发能力还是在数据安全方面InnoDB都优于MyISAM。我猜你的表也一定是用了InnoDB引擎。这就是当你的记录数越来越多的时候计算一个表的总行数会越来越慢的原因。那为什么InnoDB不跟不 MyISAM一样也把数字存起来呢这是因为即使是在同一个时刻的多个查询由于多版本并发控制MVCC的原因InnoDB表“应该返回多少行”也是不确定的。这里我用一个算count(*)的例子来为你解释一下。假设表t中现在有10000条记录我们设计了三个用户并行的会话。会话A先启动事务并查询一次表的总行数会话B启动事务插入一行后记录后查询表的总行数会话C先启动一个单独的语句插入一行记录后查询表的总行数。我们假设从上到下是按照时间顺序执行的同一行语句是在同一时刻执行的。你会看到在最后一个时刻三个会话A、B、C会同时查询表t的总行数但拿到的结果却不同。这和InnoDB的事务设计有关系可重复读是它默认的隔离级别在代码上就是通过多版本并发控制也就是MVCC来实现的。每一行记录都要判断自己是否对这个会话可见因此对于count(*)请求来说InnoDB只好把数据一行一行地读出依次判断可见的行才能够用于计算“基于这个查询”的表的总行数。不同版本的会话统计的行数是不一样的所以统计行数直接保存不支持。当然现在这个看上去笨笨的MySQL在执行count(*)操作的时候还是做了优化的。你知道的InnoDB是索引组织表主键索引树的叶子节点是数据而普通索引树的叶子节点是主键值。所以普通索引树比主键索引树小很多。对于count(*)这样的操作遍历哪个索引树得到的结果逻辑上都是一样的。因此MySQL优化器会找到最小的那棵树来遍历。在保证逻辑正 在确的前提下尽量减少扫描的数据量是数据库系统设计的通用法则之一。如果你用过showtable status 命令的话就会发现这个命令的输出结果里面也有一个TABLE_ROWS用于显示这个表当前有多少行这个命令执行挺快的那这个TABLE_ROWS能代替count(*)吗你可能还记得在第10篇文章《 MySQL为什么有时候会选错索引》中我提到过索引统计的值是通过采样来估算的。实际上TABLE_ROWS就是从这个采样估算得来的因此它也很不准。有多不准呢官方文档说误差可能达到40%到50%。所以 所 show table status s 命令显示的行 命数也不能直接使用。到这里我们小结一下MyISAM表虽然count(*)很快但是不支持事务而且带有条件的时候也是不能直接使用记录的总数的showtable status命令虽然返回很快但是不准确InnoDB表直接count(*)会遍历全表虽然结果准确但会导致性能问题。那么回到文章开头的问题如果你现在有一个页面经常要显示交易系统的操作记录总数到底应该怎么办呢答案是我们只能自己计数。接下来我们讨论一下看看自己计数有哪些方法以及每种方法的优缺点有哪些。这里我先和你说一下这些方法的基本思路你需要自己找一个地方把操作记录表的行数存起来。用缓存系统保存计数对于更新很频繁的库来说你可能会第一时间想到用缓存系统来支持。你可以用一个Redis服务来保存这个表的总行数。这个表每被插入一行Redis计数就加1每被删除一行Redis计数就减1。这种方式下读和更新操作都很快但你再想一下这种方式存在什么问题吗没错缓存系统可能会丢失更新。Redis的数据不能永久地留在内存里所以你会找一个地方把这个值定期地持久化存储起来。但即使这样仍然可能丢失更新。试想如果刚刚在数据表中插入了一行Redis中保存的值也加了1然后Redis异常重启了重启后你要从存储redis数据的地方把这个值读回来而刚刚加1的这个计数操作却丢失了。当然了这还是有解的。比如Redis异常重启以后到数据库里面单独执行一次count(*)获取真实的行数再把这个值写回到Redis里就可以了。异常重启毕竟不是经常出现的情况这一次全表扫描的成本还是可以接受的。但实际上将计数保存在缓存系统中的方式还不只是丢失更新的问题。即使 将 Redis R 正常工 正作这个值还是逻辑上不精确的。 作你可以设想一下有这么一个页面要显示操作记录的总数同时还要显示最近操作的100条记录。那么这个页面的逻辑就需要先到Redis里面取出计数再到数据表里面取数据记录。我们是这么定义不精确的一种是查到的100行结果里面有最新插入记录而Redis的计数里还没加1另一种是查到的100行结果里没有最新插入的记录而Redis的计数里已经加了1。这两种情况都是逻辑不一致的。数据库保存计数根据上面的分析用缓存系统保存计数有丢失数据和计数不精确的问题。那么如果我们把这 如个计数直接放到数据库里单独的一张计数表 个 C中又会怎么样呢首先这解决了崩溃丢失的问题InnoDB是支持崩溃恢复不丢数据的。然后我们再看看能不能解决计数不精确的问题。不同count用法这里首先你要弄清楚count()的语义。count()是一个聚合函数对于返回的结果集一行行地判断如果count函数的参数不是NULL累计值就加1否则不加。最后返回累计值。所以count(*)、count(主键id)和count(1) 都表示返回满足条件的结果集的总行数而count(字段则表示返回满足条件的数据行里面参数“字段”不为NULL的总个数。至于分析性能差别的时候你可以记住这么几个原则server层要什么就给什么InnoDB只给必要的值现在的优化器只优化了count(*)的语义为“取行数”其他“显而易见”的优化并没有做。这是什么意思呢接下来我们就一个个地来看看。对于对 count( c 主键主 id) i 来说来InnoDB引擎会遍历整张表把每一行的id值都取出来返回给server层。server层拿到id后判断是不可能为空的就按行累加。对于对 count(1) c 来说来 InnoDB引擎遍历整张表但不取值。server层对于返回的每一行放一个数字“1”进去判断是不可能为空的按行累加。单看这两个用法的差别的话你能对比出来count(1)执行得要比count(主键id)快。因为从引擎返回id会涉及到解析数据行以及拷贝字段值的操作。对 count( c 字段字 )来说来 如果这个“字段”是定义为not null的话一行行地从记录里面读出这个字段判断不能为null按行累加如果这个“字段”定义允许为null那么执行的时候判断到有可能是null还要把值取出来再判断一下不是null才累加。也就是前面的第一条原则server层要什么字段InnoDB就返回什么字段。对 count(*)来说来 但 count(*) 是例外 是 并不会把全部字段取出来而是专门做了优化不取值。count(*)肯定不是null按行累加。看到这里你一定会说优化器就不能自己判断一下吗主键id肯定非空啊为什么不能按照count(*)来处理多么简单的优化啊。当然MySQL专门针对这个语句进行优化也不是不可以。但是这种需要专门优化的情况太多了而且MySQL已经优化过count(*)了你直接使用这种用法就可以了。所以结论是按照效率排序的话count(字段)count(主键id)count(1)≈count(*)所以我建议你尽量使用count(*)。

相关新闻

永磁同步电机矢量控制C代码实战解析:S-function仿真应用与工程实践移植指南

永磁同步电机矢量控制C代码实战解析:S-function仿真应用与工程实践移植指南

永磁同步电机矢量控制C代码,全部从项目中总结得到,采用的S- 永磁同步电机矢量控制C代码,全部从项目中总结得到,采用的S-function模式仿真,与实际项目运行基本一致,可以直接复制代码移植到工程实践项目中去。…

2026/7/6 0:38:36 阅读更多 →
Spring Boot优雅停机的全面指南:原理、实践与生产级方案

Spring Boot优雅停机的全面指南:原理、实践与生产级方案

前言在微服务和云原生架构日益普及的今天,服务的启停已成为常态操作。应用的发布、扩缩容、故障迁移等场景都涉及到服务的停止。如果处理不当,粗暴地终止进程(如kill -9)可能会导致正在处理的请求被中断、数据不一致、客户端报错等…

2026/7/5 8:02:19 阅读更多 →
调试一个Windows内核驱动程序

调试一个Windows内核驱动程序

调试一个Windows内核驱动程序 我这里使用windbg来调试。 虚拟机设置 首先要在虚拟机的cmd中输入两条命令(以管理员身份运行): bcdedit /debug on bcdedit /dbgsettings serial baudrate:115200 debugport:1第一条命令表示启动调试模式&#x…

2026/7/5 9:27:42 阅读更多 →

最新新闻

PyTorch CRF 实战:BERT-CRF 命名实体识别 F1 值提升 5% 的 3 个关键点

PyTorch CRF 实战:BERT-CRF 命名实体识别 F1 值提升 5% 的 3 个关键点

PyTorch CRF 实战:BERT-CRF 命名实体识别 F1 值提升 5% 的 3 个关键点在自然语言处理领域,命名实体识别(NER)一直是一项基础而重要的任务。随着预训练语言模型如BERT的广泛应用,基于BERT的序列标注模型已成为NER的主流…

2026/7/6 0:37:25 阅读更多 →
终极指南:5分钟快速上手浏览器端人体姿态搜索工具

终极指南:5分钟快速上手浏览器端人体姿态搜索工具

终极指南:5分钟快速上手浏览器端人体姿态搜索工具 【免费下载链接】pose-search x6ud.github.io/pose-search 项目地址: https://gitcode.com/gh_mirrors/po/pose-search 想要在浏览器中实现专业级的人体姿态识别与动作搜索功能吗?pose-search是一…

2026/7/6 0:37:25 阅读更多 →
74HC32与PIC18F45K50实现高效键盘管理方案

74HC32与PIC18F45K50实现高效键盘管理方案

1. 为什么需要74HC32配合PIC18F45K50管理键盘?在嵌入式系统设计中,IO资源永远是稀缺品。传统2x2矩阵键盘需要占用4个IO口(2行2列),而采用74HC32或门芯片后,仅需2个IO即可实现4个按键的独立检测——这正是该…

2026/7/6 0:35:25 阅读更多 →
openEuler/QoS-Deployment-Test:从零开始编写自定义测试用例的完整指南

openEuler/QoS-Deployment-Test:从零开始编写自定义测试用例的完整指南

openEuler/QoS-Deployment-Test:从零开始编写自定义测试用例的完整指南 【免费下载链接】QoS-Deployment-Test Docker-based openEuler Online-Offline Co-scheduling Test Suite. 项目地址: https://gitcode.com/openeuler/QoS-Deployment-Test 前往项目官网…

2026/7/6 0:35:25 阅读更多 →
故障复盘——让失败“变成财富“

故障复盘——让失败“变成财富“

故障复盘——让失败"变成财富" 你有没有过考试错题本? 生活场景:错题本的作用 没有错题本 你考试考砸了: 错了3道题 订正了 忘了为什么错 下次考类似的,还是错 没有复盘,错误会重复。 有错题本 你考试考砸了: 错题记到本子上 分析错误原因 总结解题方法 …

2026/7/6 0:35:25 阅读更多 →
Java Web上传文件到指定目录?这招秒传逻辑绝了,调试爽到飞起

Java Web上传文件到指定目录?这招秒传逻辑绝了,调试爽到飞起

借助监控工具, 能够看到控件所提交的数据, 清晰程度极高, 调试过程极为简便。2.通过ajax向后端发送请求$.ajax({ url : "${pageContext.request.contextPath}/UploadServlet", type : "POST", data : $( #postForm).serialize(), success : function(data)…

2026/7/6 0:33:25 阅读更多 →

日新闻

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2与MySQL单元测试兼容性:5个关键SQL语句差异与规避方案1. 单元测试中的数据库兼容性挑战在Java开发领域,单元测试是保证代码质量的重要环节。当应用涉及数据库操作时,测试环境的搭建往往成为开发者的痛点。H2数据库因其轻量级、内存模式和快…

2026/7/6 0:01:17 阅读更多 →
Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘 【免费下载链接】rbtray A fork of RBTray from http://sourceforge.net/p/rbtray/code/. 项目地址: https://gitcode.com/gh_mirrors/rb/rbtray 你是否厌倦了Windows任务栏上密密麻麻的图标&…

2026/7/6 0:01:17 阅读更多 →
Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C 运行时库一键安装终极指南:告别DLL缺失烦恼 【免费下载链接】vcredist AIO Repack for latest Microsoft Visual C Redistributable Runtimes 项目地址: https://gitcode.com/gh_mirrors/vc/vcredist 你是否曾经遇到过这样的情况:下载了…

2026/7/6 0:05:19 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/5 0:03:34 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/5 0:03:34 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/5 0:07:38 阅读更多 →

月新闻