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

news2024/11/18 22:30:23

文章目录

  • 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.coloradmin.cn/o/2177729.html

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

相关文章

叶国富“推翻”马云新零售,零售新王此刻登基?

63亿入主永辉超市&#xff0c;拿到29.4%股份&#xff0c;坐上永辉超市第一大股东的宝座&#xff0c;名创优品创始人叶国富&#xff0c;成为了新科“零售之王”。 很是霸气外漏。 有投资者表示费解&#xff0c;不明白为何此时入局超市行业&#xff0c;叶国富当即召开电话会议&…

数据结构:树的定义及其性质

树的定义 树是一种重要的非线性数据结构&#xff0c;树作为一种逻辑结构&#xff0c;同时也是一种分层结构。具有以下两个特点&#xff1a; 1.树的根结点没有前驱&#xff0c;除根结点意外的节点只有一个前驱 2.树中所有结点都可以有0个或多个后继 树结构在多个领域都有广泛…

JVM相关的命令汇总

一、简介 虽然目前市场上有很多成熟的 JVM 可视化监控分析工具&#xff0c;但是所有的工具其实都依赖于 JDK 的接口和底层相关的命令&#xff0c;了解这些命令的使用对于在紧急情况下排查 JVM 相关的线上故障&#xff0c;会有更加直观的帮助。 下面一起来看看 JVM 常用的命令…

ARM硬件知识补充

一、硬件知识 1.三极管 三极管定义&#xff1a;全称应为半导体三极管&#xff0c;也被称为双极型晶体管或晶体三极管。 主要功能&#xff1a;是将微弱的电信号放大成幅度值较大的电信号&#xff0c;同时也被用作无触点开关。 分类&#xff1a;分为NPN型和PNP型两种类型。 …

GNSS定位中自适应调整电离层延迟参数过程噪声的方法

文章目录 前言一、非差非组合PPP模型二、电离层功率谱密度计算三、具体实现方法3.1 不平滑3.2 三阶多项式平滑 参考文献 前言 GNSS定位中不少技术手段如PPP和长基线RTK需要将电离层延迟作为参数估计&#xff0c;电离层延迟的变化通常被描述为随机游走过程&#xff0c;而功率谱密…

three.js 通过着色器实现热力图效果

three.js 通过着色器实现热力图效果 在线预览 https://threehub.cn/#/codeMirror?navigationThreeJS&classifyshader&idheatmapShader 在 https://threehub.cn 中还有很多案例 <!doctype html> <html lang"en"> <head> <meta charse…

RFID标签的前世今生

一、RFID标签发展历程 1.1 技术突破 RFID标签技术自20世纪90年代兴起以来&#xff0c;经历了多次重要的技术突破&#xff0c;这些突破极大地推动了RFID技术的发展和应用。 标签供电方式的革新&#xff1a;早期的RFID标签主要依赖于电磁感应线圈进行短暂供电&#xff0c;而现…

python中的assert语句

1.什么是assert 程序运行过程中,所有变量的当前值组合构成了“状态“; 每执行一段程序,状态就发生变化;如果程序有逻辑错误的bug,必定在处变量值的组合不符合预期,处于错误状态; 将对变量的预期写为断言,可以定位复杂的逻辑错误。 语法: assert <表达式> [, …

transition在vue2和vue3中的差异

背景&#xff1a; 在学习<transition/> 的时候&#xff0c;发现自己跟着视频抄写的代码&#xff0c;实现效果和示例代码不一致。 代码&#xff1a; <template><div id"app"><button id"btn" click"changeShow">切换…

Cholesky分解

Cholesky 应该怎么念&#xff0c;o(╯□╰)o&#xff0c;我感觉比较像‘瞅乐死骑’&#xff0c;毕竟这是 名字&#xff0c;哈哈哈哈 ATA 这个矩阵非常重要&#xff0c;之前在最小二乘法也见过它&#xff0c;如果&#xff1a; Axb 无解&#xff0c;也就是 xA−1b 不成立&…

随笔:创新的路径

最近&#xff0c;中国工业博览会在上海召开了&#xff0c;许多自动化行业的朋友都去参加这一场中国工业的盛会&#xff0c;兴奋地传来了各种新技术和新产品的消息。总的感觉国产产品集中度很高&#xff0c;长的都差不多。同质化现象比较严重。要实现产品的差异化&#xff0c;唯…

英伟达Blackwell系列显卡揭秘:RTX 5090与RTX 5080引领性能新高度

在万众瞩目的期待中&#xff0c;9月27日传来震撼消息&#xff0c;知名硬件信息揭秘者Kopite7kimi再次出手&#xff0c;独家披露了英伟达即将震撼登场的Blackwell系列显卡中的两款旗舰级产品——GeForce RTX 5090与RTX 5080的详细规格。这一爆料不仅揭示了英伟达新一代显卡的卓越…

企业微信:客户联系自带群发工具和聊天工具

前言 上篇博客介绍了一些客户联系的开启和配置&#xff0c;接下来我们来使用客户联系自带群发工具和聊天工具。 突然发现官方的文档已经很详细了&#xff0c;我这里给出一些简单的描述&#xff1a; 企业微信如何使用群发助手&#xff1f;-帮助中心-企业微信 群发工具 群发消息给…

Clipboard.js实现复制文本到剪贴板功能

一、Clipboard.js简介 Clipboard.js是一个轻量级的实现复制文本到剪贴板功能的JavaScript插件&#xff0c;该插件可以将输入框&#xff0c;文本域&#xff0c;DOM节点元素中的文本内容复制到剪贴板中。 官网地址&#xff1a;Clipboard.js 浏览器兼容性&#xff1a;兼容Chrome、…

第100+26步 ChatGPT学习:概率校准 Bayesian Binning into Quantiles

基于Python 3.9版本演示 一、写在前面 最近看了一篇在Lancet子刊《eClinicalMedicine》上发表的机器学习分类的文章&#xff1a;《Development of a novel dementia risk prediction model in the general population: A large, longitudinal, population-based machine-learn…

Kubernetes从零到精通(16-扩展-CRD、Custom Controller)

目录 一、简介 二、CRD 1.CRD介绍 2.CRD工作流程 三、Custom Controller 1.Custom Controller介绍 2.Custom Controller工作流程 四、示例 1.创建CR 2.配置权限RBAC 3.创建Custom Controller 3.1 Go项目初始化 3.2 main.go编写 3.3 构建镜像 3.4 部署Controller…

BaseCTF2024 个人WP

Pwn&#xff1a; [Week1] 签个到吧&#xff1a; 直接nc&#xff0c;ls&#xff0c;cat flag [Week1] echo&#xff1a; 只能使用echo命令 那就用echo *代替ls输出当前目录所有文件 用echo $(<flag)输出flag [Week1] Ret2text&#xff1a; 简单的栈溢出 from pwn impo…

10分钟,AI如何精准写出社会热点文?一篇爆款文章的背后你敢信?

本文背景 很多小伙伴们反馈&#xff0c;用AI输出的文章经常被平台判定为“疑似AI创作”&#xff0c;一但被判定&#xff0c;系统就不会给推荐流量。 到底在这个充斥着AI的大环境下&#xff0c;应该怎样完成AI文章的写作呢&#xff1f;特别是做流量主项目的小伙伴们&#xff0c;…

golang web笔记-3.响应ResponseWriter

简介 从服务器向客户端返回响应需要使用 ResponseWriter&#xff0c;ResponseWriter是一个接口&#xff0c;handler用它来返回响应。 ResponseWriter常用方法 Write&#xff1a;接收一个byte切片作为参数&#xff0c;然后把它写入到响应的body中。如果Write被调用时&a…

linux conda 安装 配置

linux conda 安装 配置 1. 下载2. 安装3. 配置&#xff08;针对非root用户&#xff0c;当前用户&#xff09; 1. 下载 网址&#xff1a;https://docs.anaconda.com/miniconda/ 下载一个.sh文件 2. 安装 命令&#xff1a;sh Miniconda3-latest-Linux-x86_64.sh 按照提示回车/…