目录

前言

COUNT(*)与 COUNT(1)

MyISAM引擎中的COUNT(*)与 COUNT(1)

Innodb引擎中的COUNT(*)与 COUNT(1)

实验

原理

结论

Count(1)、Count(2)与Count(‘anything’)

COUNT(DISTINCT expr,[expr...])

拓展


前言

三者有何区别,先给出结论:

  • count(field)不包含字段值为NULL的记录。
  • count(*)包含NULL记录。
  • count(*)与count(1) 在InnoDB中性能没有任何区别,处理方式相同
  • count(0)=count(1)=count(2)......=count(非Null值)

COUNT(*)与 COUNT(1)

如果问一个程序员MySQL中SELECT COUNT(1)和SELECT COUNT(*)有什么区别,会有很多人给出这样的答案“SELECT COUNT(*)”最终会转化成“SELECT COUNT(1),而SELECT COUNT(1)省略了转换的这一步,所以SELECT COUNT(1)效率更高“,甚至有一些面试官也会给出类似的答案。最近在看一些历史遗留代码,绝大多数统计数量的SQL都在用SELECT COUNT(1),觉得有必要搞清楚这个问题。

首先,以我们最常见的两种数据库表引擎MyISAM和Innodb来讲。

MyISAM引擎中的COUNT(*)与 COUNT(1)

官方文档解释如下:

For MyISAM tables, COUNT(*) is optimized to return very quickly if the SELECT retrieves from one table, no other columns are retrieved, and there is no WHERE clause. For example:

mysql> SELECT COUNT(*) FROM student; 

This optimization only applies to MyISAM tables, because an exact row count is stored for this storage engine and can be accessed very quickly. COUNT(1) is only subject to the same optimization if the first column is defined as NOT NULL.

MyISAM在统计表的总行数的时候会很快,但是有个大前提,不能加有任何WHERE条件。这是因为:MyISAM对于表的行数做了优化,具体做法是有一个变量存储了表的行数,如果查询条件没有WHERE条件则是查询表中一共有多少条数据,MyISAM可以做到迅速返回,仅当第一列定义为NOT NULL时,COUNT(1)才收到相同的优化。所以也解释了如果加WHERE条件,则该优化就不起作用了。细心的同学会发现,innodb的表也有这么一个存储了表行数的变量,但是很遗憾这个值是一个估计值,没有什么实际意义。

Innodb引擎中的COUNT(*)与 COUNT(1)

在该引擎下,COUNT(1)和COUNT(*)哪个快呢?结论是:这俩在高版本的MySQL(5.5及以后,5.1的没有考证)是没有什么区别的,也就没有COUN(1)会比COUNT(*)更快这一说了。

官方文档解释如下:

For transactional storage engines such as InnoDB, storing an exact row count is problematic. Multiple transactions may be occurring at the same time, each of which may affect the count.

InnoDB does not keep an internal count of rows in a table because concurrent transactions might “see” different numbers of rows at the same time. Consequently, SELECT COUNT(*) statements only count rows visible to the current transaction.

To process a SELECT COUNT(*) statement, InnoDB scans an index of the table, which takes some time if the index is not entirely in the buffer pool. For a faster count, create a counter table and let your application update it according to the inserts and deletes it does. However, this method may not scale well in situations where thousands of concurrent transactions are initiating updates to the same counter table. If an approximate row count is sufficient, use SHOW TABLE STATUS.

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

在InnoDB中性能没有任何区别,处理方式相同

WHY?这就要从COUNT()函数的具体含义说起了。”

COUNT()有两个非常不同的作用:它可以统计某个列值的数量,也可以统计行数。在统计列值时要求列值是非空的(不统计NULL)。如果在COUNT()的括号中定了列或者列表达式,则统计的就是这个表达式有值的结果数。......COUNT()的另一个作用是统计结果集的行数。当MySQL确认括号内的表达式值不可能为空时,实际上就是在统计行数。最简单的就是当我们使用COUNT(*)的时候,这种情况下通配符*并不像我们猜想的那样扩展成所有的列,实际上,他会忽略所有列而直接统计所有的行数“——《高性能MySQL》。

问题是Innodb是通过主键索引来统计行数的吗?结论是:如果该表只有一个主键索引,没有任何二级索引的情况下,那么COUNT(*)和COUNT(1)都是通过通过主键索引来统计行数的。如果该表有二级索引,则COUNT(1)和COUNT(*)都会通过占用空间最小的字段的二级索引进行统计。

实验

第一步

新建一张基于Innodb的表,只有一个ID主键,并插入5w的测试数据,建表语句如下:

CREATE TABLE `tb_news` (`id` bigint(21) NOT NULL AUTO_INCREMENT,`title` varchar(50) NOT NULL,`content` mediumtext NOT NULL,`count_ass` char(1) NOT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=50001 DEFAULT CHARSET=utf8

这个时候执行COUNT(1)和COUNT(*)可以看到解释器的结果如下(两者一致,所以就只截了一张图),可以看到,两者都用了主键索引进行行数的统计:

第二步

新建一个二级索引title,之后在分别看一下COUNT(1)和COUNT(*)的解释器结果(两者依然完全一致),这时已经用二级索引进行统计而非主键索引:

第三步

在我们之前特地预留的一个小字段count_ass字段建一个索引,到这一步目前表中有三个索引:一个主键索引,两个二级索引。

这时候我们再看一下COUNT(1)和COUNT(*)会通过哪个索引来统计行数(两者还是一致)。

原理

目前基于磁盘的数据库或者搜索引擎(比如Lucene)的性能瓶颈主要都是在IO阶段,相比于CPU和RAM,IO操作实在太慢了,所以这类系统的优化方向也都都是类似的——尽一切可能减少IO的次数(所以很多用ES的程序在性能优化到极限的时候选择直接上SSD)。这里统计行数的操作,查询优化器的优化方向就是选择能够让IO次数最少的索引,也就是基于占用空间最小的字段所建的索引(每次IO读取的数据量是固定的,索引占用的空间越小所需的IO次数也就越少)。而Innodb的主键索引是聚簇索引(包含了KEY,除了KEY之外的其他字段值,事务ID和MVCC回滚指针)所以主键索引一定会比二级索引(包含KEY和对应的主键ID)大,也就是说在有二级索引的情况下,一般COUNT()都不会通过主键索引来统计行数,在有多个二级索引的情况下选择占用空间最小的。

如果说有张Innodb的表只有主键索引,而且记录还比较大(比如30K),则统计行的操作会非常慢,因为IO次数会很多(这里就不做实验截图了,有兴趣可以自己试一下)。一个优化方案就是预先建一个小字段并建二级索引专门用来统计行数,极端情况下这种优化速度提高上千倍也是正常的。

结论

结论就是对于COUNT(1)和COUNT(*)执行优化器的优化是完全一样的,并没有COUNT(1)会比COUNT(*)快这个说法。

Count(1)、Count(2)与Count(‘anything’)

只要在Count中指定非NULL表达式,结果没有任何区别。因此当你指定Count(*) 或者Count(1)、Count(543)或者无论Count(‘anything’)时结果都会一样,因为这些值都不为NULL。

mysql5.7.23.0中实测:

数据表:

从结果上看,count(*)、count(1)、count(543)、count("dds")统计了所有记录,包括Sid为12,Sname为null,Sage为11的记录。

COUNT(DISTINCT expr,[expr...])

官方文档解释如下:

Returns a count of the number of rows with different non-NULL expr values.

If there are no matching rows, COUNT(DISTINCT) returns 0.

mysql> SELECT COUNT(DISTINCT results) FROM student;

In MySQL, you can obtain the number of distinct expression combinations that do not contain NULL by giving a list of expressions. In standard SQL, you would have to do a concatenation of all expressions inside COUNT(DISTINCT ...).

意思就是返回具有不同非空表达式值的行数的计数。

1.在count 不重复的记录的时候能用到
比如SELECT COUNT( DISTINCT id ) FROM tablename;
就是计算talbebname表中id不同的记录有多少条

2,在需要返回记录不同的id的具体值的时候可以用
比如SELECT DISTINCT id FROM tablename;
返回talbebname表中不同的id的具体的值

3.上面的情况2对于需要返回mysql表中2列以上的结果时会有歧义
比如SELECT DISTINCT id, type FROM tablename;
实际上返回的是 id与type同时不相同的结果,也就是DISTINCT同时作用了两个字段,必须得id与tyoe都相同的才被排除了,与我们期望的结果不一样

4.这时候可以考虑使用group_concat函数来进行排除,不过这个mysql函数是在mysql4.1以上才支持的

SELECT id, type from tablename group by id;
这样貌似也可以

用distinct的时候,如果它有索引,mysql会把它转成group by的方式执行。

拓展

该案例是工作过程中遇到的一个问题跟COUNT关系不大,但是跟之前讲的原理类似所以挖出来讲一讲,因为涉及到公司的具体业务所以打了码,只需要知道这是两个完全一样的SQL,不同之处就是红框内的WHERE条件的范围(这个时间字段,暂且叫作字段time,建有一个二级索引)。而通过解释器看到,时间范围短的使用了索引,而时间范围长的并没有使用索引。

因为time是一个二级索引,innodb的二级索引的叶子节点储存结构为(key+主键ID),也就是说所有根据二级索引的查询都会进行两次查询:1,二级索引查询到主键ID;2,根据1中查到的主键ID去一级索引中查找到真实数据(由于innodb的索引是聚簇索引,因此不需要去表里找数据,这一点不适用非主键索引),第2步可能会导致一定程度的随机IO。由于上图中time跨度相比上上图中大很多,mysql的执行优化器认为在这里使用二级索引“很可能”导致大量的随机IO,所以该语句执行的时候禁用了索引。

当然遇到有些情况优化器的选择也不一定总是最优的,如果你坚持要用索引,可以通过FORCE INDEX来强制使用索引,或者用USE INDEX或者IGNORE INDEX来指定你要使用的索引。

参考资料:

1.高性能MySQL count(1)与count(*)的差别

2.Mysql中count(*),DISTINCT的使用方法和效率研究

3.Mysql count(*),count(字段),count(1)的区别

4.MySQL count(*),count(1),count(field)区别、性能差异及优化建议

Mysql之count(*),count(1),count(field)区别、性能差异相关推荐

  1. MySql 执行count(1)、count(*) 与 count(列名) 区别

    MySql 执行count(1).count(*) 与 count(列名) 区别 1. 初识 count COUNT(expr) ,返回 SELECT 语句检索的行中 expr 的值不为NULL的数量 ...

  2. count(*) count(1)与count(字段)的区别

    话不多说 MySql官方文档聚合函数地址 首先count函数的简单介绍: 上翻译: 1.COUNT(expr) ,返回SELECT语句检索的行中expr的值不为NULL的数量.结果是一个BIGINT值 ...

  3. MySQL查询count(*)、count(1)、count(field)的区别收集

    count(*)对行的数目进行计算,包含NULL count(column)对特定的列的值具有的行数进行计算,不包含NULL值. count()还有一种使用方式,count(1)这个用法和count( ...

  4. count(*),count(1)和count(field)区别

    印象中,count(key)比count(*)效率要高,因此在项目中用了count(field)的形式来统计行数.在code reivew时被指出应用count(*),于是查了下,并做了下简单测试,果 ...

  5. mysql中count(*)和count(1)和count(column)区别

    在日常的mysql使用中,我们经常会看到SELECT COUNT(*).SELECT COUNT(1)等查询语句,他们到底有什么区别呢?今天我就来总结下. 我们先从函数的含义说起: count() 统 ...

  6. mysql常量求和_Mysql之:count(*)、count(常量)、count(字段)的区别

    count函数暗藏很多玄机,学习之前先尝试回答以下几个问题吧: > 1.COUNT有几种用法? > 2.COUNT(字段名)和COUNT(*)的查询结果有什么不同? > 3.COUN ...

  7. mySQL中col是什么意思_MySQL中count(*)、count(1)和count(col)的区别汇总

    前言 count函数是用来统计表中或数组中记录的一个函数,count(*) 它返回检索行的数目, 不论其是否包含 NULL值.最近感觉大家都在讨论count的区别,那么我也写下吧:欢迎留言讨论,话不多 ...

  8. MySQL count(*)、count(1) 和count(字段)的区别以及count()查询优化手段

    MySQL的count(*).count(1) 和count(字段)的区别以及count()查询优化手段. 文章目录 1 几种count查询的区别 2 优化COUNT()查询 1 几种count查询的 ...

  9. MySQL中count(1)、count(*) 与 count(列名) 的执行区别

    执行效果: 1.count(1) and count(*) 当表的数据量大些时,对表作分析之后,使用count(1)还要比使用count()用时多了! 从执行计划来看,count(1)和count() ...

最新文章

  1. mysql数据库表类型设置_mysql数据库表的类型介绍
  2. Server Error in '/' Application. 报错
  3. 自由自在意式手工冰淇淋式的生活方式
  4. 1 睡眠唤醒_一劳永逸解决WIN10所有睡眠问题
  5. shell 用环境变量的值修改properties文件
  6. 【FTP】发布FTP服务器
  7. windows 停止nginx
  8. $_SERVER['REQUEST_URI']和$_SERVER[HTTP_X_REWRITE_URL]的区别
  9. 7 分钟全面了解位运算
  10. UI实用素材|电商购物类APP界面设计原则!
  11. 【caffe】caffe采用multistep,绘制loss曲线出错
  12. spring-retry小结
  13. 使用Ntdsutil.exe捕获系统状态数据
  14. atitit.ajax 最佳实践跟框架选型 o99
  15. 基站查询网址、软件、API接口汇总
  16. [游戏杂谈]浅谈游戏打击感
  17. 计算机维修培训教材,计算机芯片级维中心(芯片级维修培训教材)b.doc
  18. 小米iot业务_一文看懂小米2019上半年财报:IoT平台连接设备达1.96亿台
  19. MySQL运维篇之分库分表
  20. 在Git中,origin / master与origin master之间有什么区别?

热门文章

  1. matlab编写长度为100矩形序列,18春[华中师范大学]华师《Matlab基础与应用》在线作业(100分)...
  2. 成都有哪些牛逼的互联网公司?
  3. 皮一皮:叛徒可耻!!!
  4. 详解模板注入漏洞(下)
  5. 因未发项目奖金,一名程序员决定删代码泄愤
  6. 读完《Effective Java》后,总结了 50 条开发技巧
  7. SpringBoot + Mybatis + Druid + PageHelper 实现多数据源分页
  8. 死磕Java并发:J.U.C之阻塞队列:LinkedBlockingDeque
  9. html不间断滚动图片,Javascript实现图片不间断滚动的代码
  10. 黄河科技学院计算机补考好过吗,学生吐槽:重修费每学分240元不该收 黄河科技学院回应:收费合理...