count(1),count(*)与 count(‘列名‘) 的区别

文章目录

  • COUNT(expr)
  • 性能对比
    • count(*) VS count(1)
    • count(*) VS count(列名)
  • count(*)会走索引吗
  • MyISAM count优化
  • InnoDB如何处理count(*)
  • 总结

参考官方文档:
https://dev.mysql.com/doc/refman/8.4/en/aggregate-functions.html#function_count

COUNT(expr)

count的基本使用

  1. COUNT(NULL)返回0
  2. COUNT(*) 返回组中的项数。包括 NULL 值和重复项。
  3. COUNT(ALL expression) 对组中的每一行都计算 expression 并返回非空值的数量。其中expression是除 text、image 或 ntext 以外任何类型的表达式。不允许使用聚合函数和子查询。
mysql> SELECT count(*), count(1), count(c3) FROM t;
+----------+----------+-----------+
| count(*) | count(1) | count(c3) |
+----------+----------+-----------+
|        3 |        3 |         2 |
+----------+----------+-----------+
1 row in set (0.00 sec)

count(expr)返回SELECT语句检索到的行中expr的非NULL值的计数。结果是一个BIGINT值
COUNT(*)有点不同,因为它返回检索到的行数计数,无论它们是否包含NULL值。

区别如下:

  1. count(*)包括了所有的列,相当于行数,在统计结果的时候,不会忽略列值为NULL的数据行;
  2. count(1)忽略所有列,用1代表代码行,在统计结果的时候,不会忽略列值为NULL的数据行;
  3. count(‘列名’)只包括列名那一列,在统计结果的时候,会忽略列值为空(这里的空不是指空字符串或者0,而是表示NULL)的计数,即某个字段值为NULL时,不统计。

最终统计结果count(*)和count(1)是没有区别的,count(‘列名’)因为忽略了值为NULL的数据,所以可能比前两者统计的数量少;

性能对比

测试环境:windows mysql8.0.35,先不创建索引

DROP PROCEDURE IF EXISTS populate_test;
DELIMITER $$
CREATE PROCEDURE populate_test()
BEGIN
    DECLARE i INT DEFAULT 1;
    WHILE i <= 100000 DO
    	IF i % 3 = 0 THEN
    		INSERT INTO test VALUES (i, NULL);
        ELSEIF  i % 3 = 1 THEN
        	INSERT INTO test VALUES (i, CONCAT(@n, ''));
        ELSEIF i % 3 = 2 THEN
        	INSERT INTO test VALUES (i, 'this is text');
        END IF;
        SET i = i + 1;
    END WHILE;
END$$
DELIMITER ;

CALL populate_test();

count(*) VS count(1)

从执行计划可以看到:两者完全相同

mysql> EXPLAIN SELECT count(*) FROM test;
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------+
|  1 | SIMPLE      | test  | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 100375 |   100.00 | NULL  |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------+
1 row in set, 1 warning (0.00 sec)

mysql> EXPLAIN SELECT count(1) FROM test;
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------+
|  1 | SIMPLE      | test  | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 100375 |   100.00 | NULL  |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------+
1 row in set, 1 warning (0.00 sec)

再对比一下两者EXPLAIN ANALYZE的结果,也是差不多的

mysql> EXPLAIN ANALYZE SELECT count(*) FROM test\G;
*************************** 1. row ***************************
EXPLAIN: -> Count rows in test  (actual time=6.26..6.26 rows=1 loops=1)

1 row in set (0.01 sec)

mysql> EXPLAIN ANALYZE SELECT count(1) FROM test\G;
*************************** 1. row ***************************
EXPLAIN: -> Count rows in test  (actual time=6.21..6.21 rows=1 loops=1)

1 row in set (0.01 sec)

记得以前听到别人说在使用count的时候要用count(1)而不要用count(*),因为使用count(*)的时候会对所有的列进行扫描,相比而言count(1)不用扫描所有列,所以count(1)要快一些。其实这个是错误的。
下面是摘自官网的一段话:https://dev.mysql.com/doc/refman/8.4/en/aggregate-functions.html#function_count

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

两者其实没有性能差别,可以在执行EXPLAIN SELECT count(*) FROM test;之后通过SHOW WARNINGS;可以看到实际上count(*)就是count(0),如下所示:

mysql> EXPLAIN SELECT count(*) FROM test;
+----+-------------+-------+------------+-------+---------------+------+---------+------+--------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key  | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+------+---------+------+--------+----------+-------------+
|  1 | SIMPLE      | test  | NULL       | index | NULL          | b    | 403     | NULL | 100280 |   100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

mysql> SHOW WARNINGS;
+-------+------+------------------------------------------------------------------------+
| Level | Code | Message                                                                |
+-------+------+------------------------------------------------------------------------+
| Note  | 1003 | /* select#1 */ select count(0) AS `count(*)` from `mysql_learn`.`test` |
+-------+------+------------------------------------------------------------------------+
1 row in set (0.00 sec)

SELECT COUNT(*) FROM tbl_name 这条sql的查询性能,对于InnoDB来说,会使用单线程进行查询如果没有WHERE或GROUP BY等额外子句存在,InnoDB表的查询性能将针对单线程工作负载进行优化。

count(*) VS count(列名)

COUNT(*) 不需要任何参数,而且不能与 DISTINCT 一起使用。
COUNT(*) 不需要 expression 参数,因为根据定义,该函数不使用有关任何特定列的信息。
COUNT(*) 返回指定表中行数而不删除副本。它对各行分别计数。包括包含空值的行。
也就是说count()只是返回表中行数,因此在处理count()的时候只需要找到属于表的数据块块头,然后计算一下行数就行了,而不用去读取里面数据列的数据。

而对于count(col)就不一样了,mysql必须读取该列的每一行的值,然后确认下是否为NULL,然后再进行计数。因此count(*)应该是比count(col)快的

从执行计划可看到,count(col)使用了全表扫描

mysql> EXPLAIN SELECT count(b) FROM test;
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------+
|  1 | SIMPLE      | test  | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 100375 |   100.00 | NULL  |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------+
1 row in set, 1 warning (0.00 sec)

EXPLAIN ANALYZE的结果来看,两者的操作不同,count(col)多了一个Aggregate: count(test.b),花费时间不多,主要是全表扫描比较耗时

mysql> EXPLAIN ANALYZE SELECT count(*) FROM test\G;
*************************** 1. row ***************************
EXPLAIN: -> Count rows in test  (actual time=5.71..5.71 rows=1 loops=1)

mysql> EXPLAIN ANALYZE SELECT count(b) FROM test\G;
*************************** 1. row ***************************
EXPLAIN: -> Aggregate: count(test.b)  (cost=20131 rows=1) (actual time=93.8..93.8 rows=1 loops=1)
    -> Table scan on test  (cost=10094 rows=100375) (actual time=0.0303..81.9 rows=100000 loops=1)

count(*)会走索引吗

下面是摘自官网的一段话:https://dev.mysql.com/doc/refman/8.4/en/aggregate-functions.html#function_count
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.

还是以上面的test表为例,100000条数据,没有任何索引,所以是没有走索引的
在这里插入图片描述
此时添加主键索引,ALTER TABLE test ADD PRIMARY KEY (a);,可以看到使用了主键索引
在这里插入图片描述
现在再在列b上添加索引,ALTER TABLE test ADD INDEX (b);,可以看到没有使用主键索引,而是使用b辅助索引
在这里插入图片描述

这里这个最小可用的二级索引(the smallest available secondary index )怎么理解?
我们可以看到b在100000条数据中只有2种值(NULL和 this is text),所以对b列加索引,B+树只需要一个根节点即可,因此索引树是最小的。主键索引因为所有key都不同,所以索引树是最大的。

mysql> SELECT b FROM test GROUP BY b;
+--------------+
| b            |
+--------------+
| NULL         |
| this is text |
+--------------+
2 rows in set (0.00 sec)

所以默认会走 b 这个索引,也可以建议优化器走主键索引:SELECT count(*) FROM test FORCE INDEX(PRIMARY);,执行计划如下
在这里插入图片描述

MyISAM count优化

对于MyISAM表,COUNT(*)经过优化,如果SELECT从一个表中查询没有查询其他列,并且没有WHERE子句,则可以非常快速地返回。例如:SELECT COUNT(*) FROM student;。因为此存储引擎存储了精确的行数,并且可以非常快速地访问。此优化仅适用于MyISAM表,只有当第一列定义为NOT NULL时,COUNT(1)才会进行相同的优化。

CREATE TABLE `t_myisam` (
  `c1` int NOT NULL AUTO_INCREMENT,
  `c2` char(5) DEFAULT NULL,
  `c3` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`c1`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
INSERT INTO t_myisam VALUES
(NULL, 'a', NULL), (NULL, 'b', '1'), (NULL, 'c', '2');

通过执行计划可以看到,table列是NULL,表明根本没有查表的数据

mysql> EXPLAIN SELECT count(*) FROM t_myisam;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                        |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
|  1 | SIMPLE      | NULL  | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL |     NULL | Select tables optimized away |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
1 row in set, 1 warning (0.00 sec)

通过show warnings;可以看到实际上执行的是select count(0) AS count(*) from mysql_learn.t_myisam

mysql> show warnings;
+-------+------+----------------------------------------------------------------------------+
| Level | Code | Message                                                                    |
+-------+------+----------------------------------------------------------------------------+
| Note  | 1003 | /* select#1 */ select count(0) AS `count(*)` from `mysql_learn`.`t_myisam` |
+-------+------+----------------------------------------------------------------------------+
1 row in set (0.00 sec)

如果加上where条件就需要扫描表的数据才能得到count结果了

mysql> EXPLAIN SELECT count(*) FROM t_myisam WHERE c1 < 100;
+----+-------------+----------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
| id | select_type | table    | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+----------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | t_myisam | NULL       | index | PRIMARY       | PRIMARY | 4       | NULL |    3 |   100.00 | Using where; Using index |
+----+-------------+----------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

InnoDB如何处理count(*)

对于InnoDB等事务存储引擎,存储精确的行数是有问题的,因为多个事务可能同时发生,每个交易都可能影响计数。InnoDB不会在表中保留行的内部计数,因为并发事务可能会同时看到不同数量的行。因此,SELECT COUNT(*)语句仅对当前事务可见的行进行计数。

InnoDB处理 SELECT COUNT(*)语句时,如果索引记录没有完全在Buffer Pool中,会花费一些时间。如果想要更快地计数,可以在自己的应用层维护一个计数表,让您的应用程序根据插入和删除操作来更新它。然而,在数千个并发事务正在启动对同一计数器表的更新的情况下,这种方法可能无法很好地扩展。如果近似行数足够,请使用SHOW TABLE STATUS。

总结

这里把上面实验的结果总结一下:

  1. count(*)和count(1)执行的效率是完全一样的。

  2. count(*)的执行效率比count(col)高,因此可以用count(*)的时候就不要去用count(col)。

  3. count(col)的执行效率比count(distinct col)高,不过这个结论的意义不大,这两种方法也是看需要去用。

  4. 如果是对特定的列做count的话建立这个列的非聚集索引能对count有很大的帮助。
    如果经常count(*)的话则可以找一个最小的col建立非聚集索引以避免全表扫描而影响整体性能。
    在不加WHERE限制条件的情况下,COUNT(*)与COUNT(COL)基本可以认为是等价的;
    但是在有WHERE限制条件的情况下,COUNT(*)会比COUNT(COL)快非常多;

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Skyeye 云这几年的经历

前言 我是 17 年毕业的&#xff0c;之前也是在学校的实验室 (做开发的) 待了两年多时间&#xff0c;期间学了不少东西&#xff0c;学的东西也算是与时俱进了。最近两年也算是开源中国的常客了&#xff0c;每周都会保持自己项目的一个更新进度。 项目地址&#xff1a;skyeye-o…

Chainlit集成LlamaIndex实现知识库高级检索(BM25全文检索器)

检索原理 BM25Retriever类是一个基于BM25算法设计的检索器&#xff0c;它主要用于从一组文档或节点中检索出与查询最相关的文档或节点。这个类的设计目的是为了提高文本检索的效率和准确性&#xff0c;尤其是在处理大量文本数据时。 BM25&#xff08;Best Matching 25&#x…

[uni-app]小兔鲜-03多端打包上线

小程序打包 打包上线流程 打包命令: pnpm build:mp-weixin效果预览: 把打包后的文件导入微信开发者工具 (dist\build\mp-weixin)代码上传: 点击微信开发者工具的上传按钮, 上传代码,审核发布: 登录微信公众平台, 提交审核, 审核后发布辅助工具: 有些团队会使用开发辅助工具 mi…

Android OpenGLES2.0开发(三):绘制一个三角形

我们总是对陌生人太客气&#xff0c;而对亲密的人太苛刻 上一篇文章中&#xff0c;我们已经将OpenGL ES环境搭建完成。接下来我们就可以开始我们的绘图之旅了。该篇我们讲解最基本图形三角形的绘制&#xff0c;这是一切绘制的基础。在OpenGL ES的世界里一切图形都可以由三角形拼…

基于nodejs+vue的农产品销售管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏&#xff1a;Java精选实战项目…

基于微信小程序爱心领养小程序设计与实现(源码+参考文档+定制开发)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-23

计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-23 本期&#xff0c;我们对大语言模型在表情推荐, 软件安全和 自动化软件漏洞检测等方面如何应用&#xff0c;提供几篇最新的参考文章。 1 Semantics Preserving Emoji Recommendation with Large Language Mod…

[深度学习]卷积神经网络CNN

1 图像基础知识 import numpy as np import matplotlib.pyplot as plt # 图像数据 #imgnp.zeros((200,200,3)) imgnp.full((200,200,3),255) # 可视化 plt.imshow(img) plt.show() # 图像读取 imgplt.imread(img.jpg) plt.imshow(img) plt.show() 2 CNN概述 卷积层convrelu池…

分布式数据库——HBase基本操作

启动HBase: 1.启动hadoop,进入hadoop的sbin中 cd /opt/hadoop/sbin/ 2.初始化namenode hdfs namenode -format 3.启动hdfs ./start-all.sh 4.启动hbase cd /opt/hbase/bin ./start-hbase.sh 5.使用jps查看进程 jps 以下图片则是hbase启动成功~ 运行HBase ./hbase sh…

64.【C语言】再议结构体(下)(未完)

本文衔接第63篇 目录 6.复习 7.修改默认对齐数 8.结构体传参 01.传递非指针参数 02.传递指针参数(传递地址) 03.对比 9.结构体实现位段 01.位段的定义 02.格式 03.例题 答案速查 分析 前置知识:位段的内存分配 解析 若按浪费空间处理 验证 6.复习 20.【C语言…

20.1 分析pull模型在k8s中的应用,对比push模型

本节重点介绍 : push模型和pull模型监控系统对比为什么在k8s中只能用pull模型的k8s中主要组件的暴露地址说明 push模型和pull模型监控系统 对比下两种系统采用的不同采集模型&#xff0c;即push型采集和pull型采集。不同的模型在性能的考虑上是截然不同的。下面表格简单的说…

全网最全软件测试面试题(含答案解析+文档)

一、软件测试基础面试题 1、阐述软件生命周期都有哪些阶段? 常见的软件生命周期模型有哪些? 软件生命周期是指一个计算机软件从功能确定设计&#xff0c;到开发成功投入使用&#xff0c;并在使用中不断地修改、增补和完善&#xff0c;直到停止该软件的使用的全过程(从酝酿到…

smb文件夹共享设置

UOS统信三种不同场景的文件夹共享,分别是:1、UOS系统间的文件共享;2、Windows7系统访问UOS共享的文件;3、UOS系统访问Windows7共享的文件 文章目录 功能概述功能介绍第一种场景:UOS系统之间的文件共享设置步骤一:打开共享文件夹步骤二:共享管理步骤三:设置共享密码步骤…

Linux使用systemd安排定期任务的操作详解

systemd 定时器是一种替代传统 cron 的方法&#xff0c;用于安排定时任务。 systemd 定时器由两部分组成&#xff1a;一个 .service 文件和一个 .timer 文件。.service 文件定义了要执行的任务&#xff0c;而 .timer 文件设定了何时执行这个任务。 通常位于 /etc/systemd/syste…

扩散模型(2)--1

1.简介 生成模型通过学习并建模输入数据的分布&#xff0c;从而采集生成新的样木&#xff0c;该模型广泛运用于图片视频生成、文本生成和药物分子生成。扩散模型是一类概率生成模型&#xff0c;扩散模型通过向数据中逐步加入噪声来破坏数据的结构&#xff0c;然后学习一个相对应…

【Linux的内存管理】

为什么需要内存管理 分段和分页内存分段内存分页 分页情况下&#xff0c;虚拟内存如何映射到物理地址页表原理多级页表 TLB快表段页式内存管理需要为什么进程地址空间Linux的进程虚拟地址空间管理进程地址空间如何分配虚拟内存虚拟内存的管理程序编译后的二进制文件如何映射到虚…

node-rtsp-stream、jsmpeg.min.js实现rtsp视频在web端播放

1. 服务地址&#xff08;私有&#xff09;&#xff1a;https://gitee.com/nnlss/video-node-server 2.node-rtsp-stream 需要安装FFMPEG&#xff1b; 3.给推拉流做了开关&#xff0c;可借助http请求&#xff0c;有更好方式可联系&#xff1b; 4.存在问题&#xff1a; 1&…

王道-计组

4 设相对寻址的转移指令占4字节,其中第1、第2字节是操作码,第3、第4字节是相对位移量(用补码表示)。设当前PC的内容为2008H,要求转移到2001H的地址,则该转移指令第3、第4字节的内容应为______ 答案:A 解析:由于指令占4字节,取指令之后(PC)+4。第3、第4字节的内容为:2…

【从0开始自动驾驶】用python做一个简单的自动驾驶仿真可视化界面

【从0开始自动驾驶】用python做一个简单的自动驾驶仿真可视化界面 废话几句废话不多说&#xff0c;直接上源码目录结构init.pysimulator.pysimple_simulator_app.pyvehicle_config.json 废话几句 自动驾驶开发离不开仿真软件成品仿真软件种类多https://zhuanlan.zhihu.com/p/3…