【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并不是表示第一个字段,而是表示一个固定值。其实就可以想成表中有这么一个字段,这个字段就是固定值1,count(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里面取出计数,再到数据表里面取数据记录。我们是这么定义不精确的:

  1. 一种是,查到的100行结果里面有最新插入记录,而Redis的计数里还没加1;
  2. 另一种是,查到的100行结果里没有最新插入的记录,而Redis的计数里已经加了1。
    这两种情况,都是逻辑不一致的。

数据库保存计数
根据上面的分析,用缓存系统保存计数有丢失数据和计数不精确的问题。那么,如果我们把这 如个计数直接放到数据库里单独的一张计数表 个 C中,又会怎么样呢?

首先,这解决了崩溃丢失的问题,InnoDB是支持崩溃恢复不丢数据的。然后,我们再看看能不能解决计数不精确的问题。

不同count用法
这里,首先你要弄清楚count()的语义。count()是一个聚合函数,对于返回的结果集,一行行地判断,如果count函数的参数不是NULL,累计值就加1,否则不加。最后返回累计值。

所以,count(*)、count(主键id)和count(1) 都表示返回满足条件的结果集的总行数;而count(字段),则表示返回满足条件的数据行里面,参数“字段”不为NULL的总个数。

至于分析性能差别的时候,你可以记住这么几个原则:

  1. server层要什么就给什么;
  2. InnoDB只给必要的值;
  3. 现在的优化器只优化了count(*)的语义为“取行数”,其他“显而易见”的优化并没有做。

这是什么意思呢?接下来,我们就一个个地来看看。
对于对 count( c 主键主 id) i 来说来 :
InnoDB引擎会遍历整张表,把每一行的id值都取出来,返回给server层。server层拿到id后,判断是不可能为空的,就按行累加。
对于对 count(1) c 来说来 ,InnoDB引擎遍历整张表,但不取值。server层对于返回的每一行,放一个数字“1”进去,判断是不可能为空的,按行累加。
单看这两个用法的差别的话,你能对比出来,count(1)执行得要比count(主键id)快。因为从引擎返回id会涉及到解析数据行,以及拷贝字段值的操作。

对 count( c 字段字 )来说来 :

  1. 如果这个“字段”是定义为not null的话,一行行地从记录里面读出这个字段,判断不能为null,按行累加;
  2. 如果这个“字段”定义允许为null,那么执行的时候,判断到有可能是null,还要把值取出来再判断一下,不是null才累加。
    也就是前面的第一条原则,server层要什么字段,InnoDB就返回什么字段。

对 count( * )来说来 :
但 count(*) 是例外 是 ,并不会把全部字段取出来,而是专门做了优化,不取值。count(*)肯定不是null,按行累加。

看到这里,你一定会说,优化器就不能自己判断一下吗,主键id肯定非空啊,为什么不能按照count(*)来处理,多么简单的优化啊。

当然,MySQL专门针对这个语句进行优化,也不是不可以。但是这种需要专门优化的情况太多了,而且MySQL已经优化过count(*)了,你直接使用这种用法就可以了。

所以结论是:按照效率排序的话,count(字段)<count(主键id)<count(1)≈count(*),所以我建议你,尽量使用count(*)。

文章知识点与官方知识档案匹配,可进一步学习相关知识

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

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

相关文章

【SpringCloud】负载均衡(Spring Cloud LoadBalancer)

负载均衡 当服务流量增大时&#xff0c;通常会采用增加机器的方式进行扩容。负载均衡就是用来在多个机器或者其他资源中&#xff0c;按照一定的规则合理分配负载。其中的实现可以分成客户端负载均衡和服务端负载均衡。 服务端负载均衡 在服务端进行负载均衡的算法分配。 比…

数据丢失?别怕,EasyRecovery来帮忙!

&#x1f31f; 数据丢失&#xff1f;别怕&#xff0c;EasyRecovery来帮忙&#xff01; 嘿&#xff0c;亲爱的的朋友们&#xff01;&#x1f44b;今天我要和大家分享一款超棒的数据恢复软件——EasyRecovery&#xff01;&#x1f389;你是否曾经遇到过不小心删除了重要文件、照片…

zip文件上传到linux服务器文件大小发生变化

在传一个文件到服务器的时候&#xff0c;第一次传完看见大小不一样&#xff08;服务器中du命令查看大小796596MB&#xff09;就重传了一下&#xff0c;还是大小不一样&#xff0c;就查了下。 查了下有以下原因&#xff1a; 文件系统的不同&#xff1a; 原因&#xff1a;不同的…

windows 系统根据端口查找进程,杀死进程

在启动项目时&#xff0c;往往设置的端口被占用&#xff0c;这时需要杀死端口所占用的进程&#xff0c;然后再重启项目。 netstat -ano | findstr :8085 taskkill /F /PID 25184 杀死进程后&#xff0c;再执行命令 netstat -ano | findstr :8085 进行查看端口占用情况

STM32多功能交通灯系统:从原理到实现

一、功能说明 本交通灯系统采用先进的stm32f103c8t6微处理器为核心控制单元。系统设置东南西北四个方向各配置两位数码管&#xff0c;用以精准展示5至99秒的时间范围&#xff0c;并且允许用户根据实际需求进行灵活调整。 在信号灯配置方面&#xff0c;每个方向均配备左转、直…

SYD881X读取GATT VALUE的长度

SYD881X读取GATT VALUE的长度 现在具体遇到这样一个需要&#xff0c;机器生产后要更新profile&#xff0c;这个只能够通过升级4K来做&#xff0c;但是需要知道profile是否改变了&#xff0c;这个就要知道profile是否改变来决定是否要升级&#xff0c;这里的做法是增加一个函数&…

肩背筋膜炎怎么治疗最有效

肩背筋膜炎是一种常见的肌肉骨骼疾病&#xff0c;其症状主要包括&#xff1a;肩背区域疼痛&#xff1a;由于筋膜组织受到损伤&#xff0c;肩背部位会出现明显的疼痛&#xff0c;疼痛可能会放射到周围的其他部位&#xff0c;严重时会影响睡眠和休息。肌肉紧张和僵硬&#xff1a;…

嵌入式数据库_2.嵌入式数据库的一般架构

嵌入式数据库的架构与应用对象紧密相关&#xff0c;其架构是以内存、文件和网络等三种方式为主。 1.基于内存的数据库系统 基于内存的数据库系统中比较典型的产品是每个McObject公司的eXtremeDB嵌入式数据库&#xff0c;2013年3月推出5.0版&#xff0c;它采用内存数据结构&…

智能座舱中的HUD介绍

HUD&#xff08;Head Up Display&#xff0c;抬头显示系统&#xff09;是将车速、油耗、胎压等行车重要信息投影到前方挡风玻璃上的一套显示系统。HUD 最早应用在战斗机上&#xff0c;旨在降低飞行员低头查看仪表的频率&#xff0c;使得飞行员能在保证正常驾驶的同时&#xff0…

2002-2022年各省人口总抚养比数据(人口抽样调查)

2002-2022年各省人口总抚养比数据&#xff08;人口抽样调查&#xff09; 1、时间&#xff1a;2002-2022年 2、指标&#xff1a;总抚养比 3、来源&#xff1a;国家统计局、统计年鉴 4、范围&#xff1a;31省&#xff0c; 5、缺失情况&#xff1a;无缺失&#xff0c;其中201…

Git 和 TortoiseGit 安装和配置(图文详解)

使用git&#xff0c;需要在Windows上需要安装两个软件&#xff1a;1&#xff09;Git 2&#xff09;TortoiseGit 注意&#xff1a;tortoiseGit是在安装了Git的基础上运行的&#xff0c;所以需要先安装Git&#xff0c;后安装运行tortoiseGit。 因为&#xff0c;运行TortoiseGi…

Web3新视野:Lumoz节点的潜力与收益解读

摘要&#xff1a;低估值、高回报、无条件退款80%...... Lumoz正通过其 zkVerifier 节点销售活动&#xff0c;引领一场ZK计算革命。 长期以来&#xff0c;加密市场以其独特的波动性和增长潜力&#xff0c;持续吸引着全球投资者的目光。而历史数据表明&#xff0c;市场往往在一年…

六西格玛目标设定的时候需要考虑哪些因素?

在追求企业卓越绩效的道路上&#xff0c;六西格玛管理方法论以其严谨的数据驱动和持续改进的理念&#xff0c;成为众多企业的首选工具。然而&#xff0c;要想真正发挥六西格玛的潜力&#xff0c;合理而精准的目标设定至关重要。那么&#xff0c;六西格玛目标设定的时候需要考虑…

电商API接口详述:涵盖订单、库存等多功能接口介绍

电商商家自研管理系统&#xff0c;线下ERP系统或WMS系统想要接入电商平台订单打单发货&#xff0c;通过点三电商API可以一键对接多个电商平台&#xff0c;帮助商家、ERP/WMS服务商快速开发电商模块&#xff0c;实现电商业务管理功能&#xff0c;那么点三电商API接口有哪些可用接…

Stranger Lands - StampIT

作为StampIT!Collection的一部分,Stranger Lands包含30个高质量纹理,具有惊人的4K分辨率。这些高度图纹理可以用作Unity地形工具的画笔,以便在Unity地形上绘制特征。或者您可以在Atlas、Gaia、Map Magic 2、Vista等其他资源中使用这些高度图纹理。 该资源包含高度图和笔刷预…

Java多线程+线程池图文实例操作(源码自取)

目录 线程相关概念 并发 并行 继承Thread类 实现Runnable接口 实现Callable接口 使用ExecutorService 和线程池 多线程卖手机 非同步 同步机制卖手机 锁方法 锁代码块 ​编辑锁静态方法 锁静态代码块 线程常用方法 用户线程和守护线程 线程状态 线程池 自定…

视频监控管理平台的日志功能的重要性

日志功能的重要性 视频监控平台在日常工作生活中越来越重要&#xff0c;具有完备的平台日志&#xff0c;不仅可以增强视频监控系统的自身安全性&#xff0c;还能在更大程度上保障社会的安全与稳定。 &#xff08;一&#xff09;安全保障 视频监控平台作为安全防护…

第三十三章 添加和使用自定义标题元素

文章目录 第三十三章 添加和使用自定义标题元素SOAP 标头元素简介如何表示 SOAP 标头 第三十三章 添加和使用自定义标题元素 本主题介绍如何添加和使用自定义 SOAP 标头元素。 有关发生故障时添加标头元素的信息&#xff0c;请参阅 SOAP 故障处理。 WS-Addressing 标头元素在…

机器学习课程复习——集成学习

1. 基本概念 1.1. 定义 通过构建并结合多个个体学习器来完成学习任务,获得比单一学习器显著优越的泛化性能。 1.2. 分类 名称个体学习器例子同质集成基学习器Boosting、Bagging异质集成组件学习器Stacking1.3. 研究的核心 个体学习器的“准确性”和“多样性”本身就存在冲…

悬浮翻译app免费怎么操作?看完这篇文章就知道了

#高考结束该出发看世界了# 高考的钟声已落&#xff0c;是时候开启探索世界的旅程了。无论是踏上旅途&#xff0c;观看视频&#xff0c;阅读书籍&#xff0c;还是浏览网页资料&#xff0c;我们都有机会拓宽视野。 然而&#xff0c;语言常常成为我们与世界沟通的障碍。好在&…