MySQL Count函数使用教程

目录
  • 一、COUNT 的使用
  • 二、COUNT是怎么样运行的
  • 三、COUNT函数的索引使用情况
  • 四、补充
  • 五、总结

COUNT 是一个汇总函数(聚集函数),它接受一个表达式作为参数:

COUNT(expr)

COUNT函数用于统计在符合搜索条件的记录中,指定的表达式expr不为NULL的行数有多少。这里需要特别注意的是,expr不仅仅可以是列名,其他任意表达式都是可以的。

一、COUNT 的使用

select COUNT(key1) FROM t;

这个语句是用于统计在 t 表 key1 列 值不为 NULL 的行数是多少。

看下面的这个:

select COUNT('abc') FROM t;

这个语句是用于统计在 t 表的所有记录中,‘abc’ 这个表达式不为 NULL的行数是多少。很显然,‘abc’ 这个表达式永远不为 NULL, 所以上述语句其实就是统计 t 表里有多少条记录。

再看这个:

select COUNT(*) from t;

这个语句就是直接统计 t 表中有多少条记录。

总结 + 注意:COUNT函数的参数可以是任意表达式, 该函数用于统计在符合搜索条件的记录中,指定的表达式不为NULL的行数有多少。

二、COUNT是怎么样运行的

mysql> select count(*) from single_table;
+----------+
| count(*) |
+----------+
|    12610 |
+----------+
1 row in set (0.00 sec)
####### single_table 表结构 ########
CREATE TABLE `single_table` (
  `id` int NOT NULL AUTO_INCREMENT,
  `key1` varchar(100) DEFAULT NULL,
  `key2` int DEFAULT NULL,
  `key3` varchar(100) DEFAULT NULL,
  `key_part1` varchar(100) DEFAULT NULL,
  `key_part2` varchar(100) DEFAULT NULL,
  `key_part3` varchar(100) DEFAULT NULL,
  `common_field` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_key2` (`key2`),
  KEY `idx_key1` (`key1`),
  KEY `idx_key3` (`key3`),
  KEY `idx_key_part` (`key_part1`,`key_part2`,`key_part3`)
) ENGINE=InnoDB AUTO_INCREMENT=20000 DEFAULT CHARSET=utf8mb3 |

这个语句是要去查询表 single_table 中共包含多少条记录。由于聚簇索引和二级索引中的记录是一一对应的,而二级索引记录中包含的列是少于聚簇索引记录的,所以同样数量的二级索引记录可以比聚簇索引记录占用更少的存储空间。如果我们使用二级索引执行上述查询,即数一下idx_key2中共有多少条二级索引记录(存在多个二级索引,为什么选择idx_key2,下面会具体说明),是比直接数聚簇索引中共有多少聚簇索引记录可以节省很多I/O成本。所以优化器会决定使用idx_key2执行上述查询。

mysql> explain select count(*) from single_table;
+----+-------------+--------------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
| id | select_type | table        | partitions | type  | possible_keys | key      | key_len | ref  | rows  | filtered | Extra       |
+----+-------------+--------------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
|  1 | SIMPLE      | single_table | NULL       | index | NULL          | idx_key2 | 5       | NULL | 12590 |   100.00 | Using index |
+----+-------------+--------------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

在执行上述查询时,server层会维护一个名叫count的变量,然后:

(1)server层向InnoDB要第一条记录。

(2)InnoDB找到idx_key1的第一条二级索引记录,并返回给server层(注意:由于此时只是统计记录数量,所以并不需要回表)。

(3)由于COUNT函数的参数是*,MySQL会将*当作常数0处理。由于0并不是NULL,server层给count变量加1。

(4)server层向InnoDB要下一条记录。

(5)InnoDB通过二级索引记录的next_record属性找到下一条二级索引记录,并返回给server层。

(6)server层继续给count变量加1。

(7)... 重复上述过程,直到InnoDB向server层返回没记录可查的消息。

(8)server层将最终的count变量的值发送到客户端。

三、COUNT函数的索引使用情况

下面我们增对 count(*),count(1),count(常数),count(主键列),count(普通列(有索引)),count(普通列(无索引))

(1)count(*),count(1),count(常数)

mysql> show create table single_table;
+--------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table        | Create Table                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
+--------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| single_table | CREATE TABLE `single_table` (
  `id` int NOT NULL AUTO_INCREMENT,
  `key1` varchar(100) DEFAULT NULL,
  `key2` int DEFAULT NULL,
  `key3` varchar(100) DEFAULT NULL,
  `key_part1` varchar(100) DEFAULT NULL,
  `key_part2` varchar(100) DEFAULT NULL,
  `key_part3` varchar(100) DEFAULT NULL,
  `common_field` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_key2` (`key2`),
  KEY `idx_key1` (`key1`),
  KEY `idx_key3` (`key3`),
  KEY `idx_key_part` (`key_part1`,`key_part2`,`key_part3`)
) ENGINE=InnoDB AUTO_INCREMENT=20000 DEFAULT CHARSET=utf8mb3 |
+--------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> select count(*) from single_table;
+----------+
| count(*) |
+----------+
|    12610 |
+----------+
1 row in set (0.00 sec)
## count(*) 采用了 idx_key2 索引
mysql> explain select count(*) from single_table;
+----+-------------+--------------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
| id | select_type | table        | partitions | type  | possible_keys | key      | key_len | ref  | rows  | filtered | Extra       |
+----+-------------+--------------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
|  1 | SIMPLE      | single_table | NULL       | index | NULL          | idx_key2 | 5       | NULL | 12590 |   100.00 | Using index |
+----+-------------+--------------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
## count(1) 采用了 idx_key2 索引
mysql> explain select count(1) from single_table;
+----+-------------+--------------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
| id | select_type | table        | partitions | type  | possible_keys | key      | key_len | ref  | rows  | filtered | Extra       |
+----+-------------+--------------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
|  1 | SIMPLE      | single_table | NULL       | index | NULL          | idx_key2 | 5       | NULL | 12590 |   100.00 | Using index |
+----+-------------+--------------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
## count('abc') 采用了 idx_key2 索引
mysql> explain select count('abc') from single_table;
+----+-------------+--------------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
| id | select_type | table        | partitions | type  | possible_keys | key      | key_len | ref  | rows  | filtered | Extra       |
+----+-------------+--------------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
|  1 | SIMPLE      | single_table | NULL       | index | NULL          | idx_key2 | 5       | NULL | 12590 |   100.00 | Using index |
+----+-------------+--------------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

通过上述查询结果可以看出:

count(*)、count(1)、count('abc') 均采用了 idx_key2,而索引idx_key2 对应的索引列为 key2,字段类型为 int,占用空间为最小的索引列。

结论:

对于 COUNT(*)、COUNT(1) 或者任意的 COUNT(常数) 来说,读取哪个索引的记录其实并不重要,因为server层只关心存储引擎是否读到了记录,而并不需要从记录中提取指定的字段来判断是否为NULL。所以优化器会使用占用存储空间最小的那个索引来执行查询。

(2)count(主键列)

## count(id) 采用了 idx_key2 索引
mysql> explain select count(id) from single_table;
+----+-------------+--------------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
| id | select_type | table        | partitions | type  | possible_keys | key      | key_len | ref  | rows  | filtered | Extra       |
+----+-------------+--------------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
|  1 | SIMPLE      | single_table | NULL       | index | NULL          | idx_key2 | 5       | NULL | 12590 |   100.00 | Using index |
+----+-------------+--------------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

通过上述查询结果可以看出:

count(id)采用了 idx_key2,而索引idx_key2 对应的索引列为 key2,字段类型为 int,占用空间为最小的索引列。

结论:

对于 COUNT(id) 来说,由于id是主键,不论是聚簇索引记录,还是任意一个二级索引记录中都会包含主键字段,所以其实读取任意一个索引中的记录都可以获取到id字段,此时优化器也会选择占用空间最小的那个索引来执行查询。

(3)count(普通列(有索引))

## count('key1') 采用了 idx_key1 索引
mysql> explain select count(key1) from single_table;
+----+-------------+--------------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
| id | select_type | table        | partitions | type  | possible_keys | key      | key_len | ref  | rows  | filtered | Extra       |
+----+-------------+--------------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
|  1 | SIMPLE      | single_table | NULL       | index | NULL          | idx_key1 | 303     | NULL | 12590 |   100.00 | Using index |
+----+-------------+--------------+------------+-------+---------------+----------+---------+------+-------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
## count(common_field) 未采用任何索引
mysql> explain select count(common_field) from single_table;
+----+-------------+--------------+------------+------+---------------+------+---------+------+-------+----------+-------+
| id | select_type | table        | partitions | type | possible_keys | key  | key_len | ref  | rows  | filtered | Extra |
+----+-------------+--------------+------------+------+---------------+------+---------+------+-------+----------+-------+
|  1 | SIMPLE      | single_table | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 12590 |   100.00 | NULL  |
+----+-------------+--------------+------------+------+---------------+------+---------+------+-------+----------+-------+
1 row in set, 1 warning (0.00 sec)

通过上述查询结果可以看出:

count(key1)采用了 idx_key1,索引idx_key1对应的索引列即为key1。count(common_field)未采用任何索引,common_field也不存在任何索引。

结论:

对于COUNT(非主键列)来说,我们指定的列可能并不会包含在每一个索引中。这样优化器只能选择包含我们指定的列的索引去执行查询,这就可能导致优化器选择的索引并不是最小的那个。

四、补充

对于count(非空普通列)来说,使用索引情况会怎么样?会不会直接采用最小占用空间索引呢?

mysql> show create table person_info;
+-------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table       | Create Table                                                                                                                                                                                                                                                                                                                                                     |
+-------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| person_info | CREATE TABLE `person_info` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  `birthday` date NOT NULL,
  `age` int DEFAULT NULL,
  `phone_number` char(11) NOT NULL,
  `country` varchar(100) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`),
  KEY `idx_age` (`age`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb3 |
+-------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> explain select count(phone_number) from person_info;
+----+-------------+-------------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table       | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra |
+----+-------------+-------------+------------+------+---------------+------+---------+------+------+----------+-------+
|  1 | SIMPLE      | person_info | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    9 |   100.00 | NULL  |
+----+-------------+-------------+------------+------+---------------+------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

通过上述查询结果可以看出:

虽然 phone_number 字段为 not null,count(phone_number) 和 count(*) 结果一致,但是 phone_number 仍然并有选择走索引。

五、总结

(1)对于COUNT(*)、COUNT(常数)、COUNT(主键) 形式的COUNT函数来说,优化器可以选择最小索引执行查询,从而提升效率,它们的执行过程是一样的,只不过在判断表达式是否为NULL时选择不同的判断方式,这个判断为NULL的过程的代价可以忽略不计,所以我们可以认为 COUNT(*)、COUNT(常数)、COUNT(主键) 所需要的代价是相同的。

(2)对于 COUNT(非主键列) 来说,server层必须要从InnoDB中读到包含非主键列的记录,所以优化器并不能随心所欲的选择占用空间最小的索引去执行。

到此这篇关于MySQL Count函数使用教程的文章就介绍到这了,更多相关MySQL Count内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • MySql统计函数COUNT的具体使用详解

    目录 1. COUNT()函数概述 2. COUNT()参数说明 3. COUNT()判断存在 4. COUNT()阿里开发规范 1. COUNT()函数概述 COUNT() 是一个聚合函数,返回指定匹配条件的行数.开发中常用来统计表中数据,全部数据,不为NULL数据,或者去重数据. 2. COUNT()参数说明 COUNT(1):统计不为NULL 的记录.COUNT(*):统计所有的记录(包括NULL). COUNT(字段):统计该"字段"不为NULL 的记录.1.如果这个字段是定义

  • 5招带你轻松优化MySQL count(*)查询性能

    目录 前言 1 count(*)为什么性能差 2 如何优化count(*)性能 2.1 增加redis缓存 2.2 加二级缓存 2.3 多线程执行 2.4 减少join的表 2.5 改成ClickHouse 3 count的各种用法性能对比 前言 最近我在公司优化过几个慢查询接口的性能,总结了一些心得体会拿出来跟大家一起分享一下,希望对你会有所帮助. 我们使用的数据库是Mysql8,使用的存储引擎是Innodb.这次优化除了优化索引之外,更多的是在优化count(*). 通常情况下,分页接口一般

  • MySQL COUNT(*)性能原理详解

    目录 前言 1.COUNT(1).COUNT(*)与COUNT(字段)哪个更快? 实验分析 实验结果 实验结论 2.COUNT(*)与TABLES_ROWS 3.COUNT(*)是怎么样执行的? 4.总结 前言 在实际开发过程中,统计一个表的数据量是经常遇到的需求,用来统计数据库表的行数都会使用COUNT(*),COUNT(1)或者COUNT(字段),但是表中的记录越来越多,使用COUNT(*)也会变得越来越慢,今天我们就来分析一下COUNT(*)的性能到底如何. 1.COUNT(1).COUN

  • MySQL性能之count* count1 count列对比示例

    目录 正文 count() 性能与啥相关? MVCC 简介 MySQL 对 count() 的优化 查询性能 PK 大起底 count(主键id) count(1) count(字段) count(*) count(1) 和 count(*) 对比 总结 正文 最近的工作中,我听到组内两名研发同学在交流数据统计性能的时候,聊到了以下内容: 数据统计你怎么能用 count(*) 统计数据呢,count(*) 太慢了,要是把数据库搞垮了那不就完了么,赶紧改用 count(1),这样比较快......

  • Mysql中使用count加条件统计的实现示例

    目录 前言 测试环境 准备工作 条件统计 总结 前言 最近发现在处理Mysql问题时,count()函数频繁上镜,常常出现在分组统计的情景下,但是有时候并不是使用group by分好组就可以直接统计了,比如说一个常见的需求,统计每个班级男生所占的比例,这种情况一般会按照班级分组,但是分组内不但要统计班级的人数,还要统计男生的人数,也就是说统计是有条件的,之前确实没有考虑过怎样实心,后来查询了资料,总结在这里,方便日后查找使用. Mysql中count()函数的一般用法是统计字段非空的记录数,所以

  • MySQL count(*)统计总数问题汇总

    目录 1. MyISAM存储引擎计数为什么这么快? 2. 能不能手动实现统计总行数 3. InnoDB引擎能否实现快速计数 在日常开发工作中,我经常会遇到需要统计总数的场景,比如:统计订单总数.统计用户总数等.一般我们会使用MySQL 的count函数进行统计,但是随着数据量逐渐增大,统计耗时也越来越长,最后竟然出现慢查询的情况,这究竟是什么原因呢?本篇文章带你一下学习一下. 1. MyISAM存储引擎计数为什么这么快? 我们总有个错觉,就是感觉MyISAM引擎的count计数要比InnoDB引

  • MySQL select count(*)计数很慢优化方案

    目录 前言 1. MyISAM存储引擎计数为什么这么快? 2. 能不能手动实现统计总行数 3. InnoDB引擎能否实现快速计数 4. 四种计数方式的性能差别 前言 在日常开发工作中,我经常会遇到需要统计总数的场景,比如:统计订单总数.统计用户总数等.一般我们会使用MySQL 的count函数进行统计,但是随着数据量逐渐增大,统计耗时也越来越长,最后竟然出现慢查询的情况,这究竟是什么原因呢?本篇文章带你一下学习一下. 1. MyISAM存储引擎计数为什么这么快? 我们总有个错觉,就是感觉MyIS

  • mysql count 为null时,显示0的问题

    目录 mysql count 为null时,显示0 1.使用ifnull 2.运行结果 mysql让count为0的记录也显示出来 在mysql 下执行如下命令 mysql count 为null时,显示0 1.使用ifnull ifnull(字段名,目标值) SELECT a.*,IFNULL(r.count,0) from (SELECT act_id,poster_id,type,style_type,status,status_time,title,content,images,star

  • MySQL Count函数使用教程

    目录 一.COUNT 的使用 二.COUNT是怎么样运行的 三.COUNT函数的索引使用情况 四.补充 五.总结 COUNT 是一个汇总函数(聚集函数),它接受一个表达式作为参数: COUNT(expr) COUNT函数用于统计在符合搜索条件的记录中,指定的表达式expr不为NULL的行数有多少.这里需要特别注意的是,expr不仅仅可以是列名,其他任意表达式都是可以的. 一.COUNT 的使用 select COUNT(key1) FROM t; 这个语句是用于统计在 t 表 key1 列 值不

  • MySQL COUNT函数的使用与优化

    COUNT 函数做什么用? COUNT 是一个专用的函数,通常有两种不同的方式:计算值和数据行.值指的是非空(Non-NULL)表达式(NULL表示值缺失).如果我们在 COUNT的参数中指定了列名或其他表达式,则 COUNT 函数是计算该表达式拥有值的次数.这让很多人困惑,相当一部分的原因是值和 NULL 的概念是模糊的. 另一种 COUNT 的形式是简单地计算结果集的数据行数.这是在 MySQL 知道 COUNT 函数参数的表达式不可能为 NULL 时的计算方式.最为典型的例子是 COUNT

  • 详解 MySQL中count函数的正确使用方法

    1. 描述 在MySQL中,当我们需要获取某张表中的总行数时,一般会选择使用下面的语句 select count(*) from table; 其实count函数中除了*还可以放其他参数,比如常数.主键id.字段,那么它们有什么区别?各自效率如何?我们应该使用哪种方式来获取表的行数呢? 当搞清楚count函数的运行原理后,相信上面几个问题的答案就会了然于胸. 2. 表结构 为了解决上述的问题,我创建了一张 user 表,它有两个字段:主键id和name,后者可以为null,建表语句如下. CRE

  • mysql count详解及函数实例代码

    mysql count详解 count函数是用来统计表中或数组中记录的一个函数,下面我来介绍在mysql中count函数用法. count(*) 它返回检索行的数目, 不论其是否包含 NULL值. SELECT 从一个表中检索,而不检索其它的列,并且没有 WHERE子句时, COUNT(*)被优化到最快的返回速度. 例如: mysql> SELECT COUNT(*) FROM student; COUNT(DISTINCT 字段)这个优化仅适用于 MyISAM表, 原因是这些表类型会储存一个函

  • 详细解读MySQL中COUNT函数的用法

    MySQL的COUNT函数是最简单的功能,非常有用的计算,预计由一个SELECT语句返回的记录数. 要了解COUNT函数考虑的EMPLOYEE_TBL的的表具有以下记录: mysql> SELECT * FROM employee_tbl; +------+------+------------+--------------------+ | id | name | work_date | daily_typing_pages | +------+------+------------+----

  • mysql常用函数之group_concat()、group by、count()、case when then的使用

    目录 场景: 一.行转列函数 group_concat(arg) 二.分组 group by.count().sum() 函数的组合使用 三.count() 配合 case when then 的使用 场景: 在mysql的关联查询或子查询中,函数 group_concat(arg) 可以合并多行的某列(或多列)数据为一行,默认以逗号分隔.以及分组函数和统计函数的组合使用 测试数据准备: 一.行转列函数 group_concat(arg) 1.单列合并,默认以逗号分隔 select group_

  • MySQL中的CONCAT函数使用教程

    使用MySQL CONCAT()函数将两个字符串连接起来,形成一个单一的字符串.试试下面的例子: mysql> SELECT CONCAT('FIRST ', 'SECOND'); +----------------------------+ | CONCAT('FIRST ', 'SECOND') | +----------------------------+ | FIRST SECOND | +----------------------------+ 1 row in set (0.00

  • MYSQL updatexml()函数报错注入解析

    首先了解下updatexml()函数 UPDATEXML (XML_document, XPath_string, new_value); 第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc 第二个参数:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程. 第三个参数:new_value,String格式,替换查找到的符合条件的数据 作用:改变文档中符合条件的节点的值 改变XML_document中符合X

  • MySQL curdate()函数的实例详解

    MySQL CURDATE功能介绍 如果在数字上下文中使用字符串上下文或YYYMMDD格式,CURDATE()函数将以"YYYY-MM-DD"格式的值返回当前日期. 以下示例显示了如何在字符串上下文中使用CURDATE()函数. sql> SELECT CURDATE(); +------------+ | CURDATE() | +------------+ | 2017-08-10 | +------------+ 1 row in set (0.00 sec) 以下示例说明

  • MySQL 字符串函数大全

    MySQL 字符串函数大全 对于针对字符串位置的操作,第一个位置被标记为1. ASCII(str) 返回字符串str的最左面字符的ASCII代码值.如果str是空字符串,返回0.如果str是NULL,返回NULL. mysql> select ASCII('2'); -> 50 mysql> select ASCII(2); -> 50 mysql> select ASCII('dx'); -> 100 也可参见ORD()函数. ORD(str) 如果字符串str最左面

随机推荐