龙空技术网

MySQL的count()分析

淼淼墩 101

前言:

现时同学们对“mysql的count”可能比较看重,我们都想要学习一些“mysql的count”的相关知识。那么小编同时在网上网罗了一些有关“mysql的count””的相关资讯,希望你们能喜欢,同学们快快来学习一下吧!

在我们的日常开发工作中,数据库的增删改查操作可能是最基础也是使用最广泛的;数据库查询是数据基本操作中必不可少的,而在查询数据时首先会返回数据数量,也就是我们这节要讨论的count()函数了,下面将详细介绍count()函数的几种语法以及不同语法之间的区别...

首先,这里收集了一些面试过程中经常遇到的有关count()语句的相关问题,如下所示:

1. COUNT有几种用法?

2. COUNT(字段名)和COUNT(*)的查询结果有什么不同?

3. COUNT(1)和COUNT(*)之间有什么不同?

4. COUNT(1)和COUNT(*)之间的效率哪个更高?

5. 为什么《阿里巴巴Java开发手册》建议使用COUNT(*)?

6. MySQL的MyISAM引擎对COUNT(*)做了哪些优化?

7. MySQL的InnoDB引擎对COUNT(*)做了哪些优化?

8. 上面提到的MySQL对COUNT(*)做的优化,有一个关键的前提是什么?

9. SELECT COUNT(*) 的时候,加不加where条件有差别吗?

10. COUNT(*)、COUNT(1)和COUNT(字段名)的执行过程是怎样的?

想必这些问题大家在面试过程中也遇到过,如果大家对以上面试题目回答全部准确无误的话,那可以说是了解的相当透彻了;如果还有一些不了解的话,那下文将给大家答疑解悟;

初识count()

COUNT()函数返回表中的行数。 COUNT()函数允许您对表中符合特定条件的所有行进行计数。

COUNT()函数的语法如下:COUNT(expression)。

count(*)、count(常量)和count(列名)的区别:

在《阿里巴巴Java开发手册》中,强制建议不能使用count(1)和count(列名)来代替count(*),那这是为什么呢?它们之间的区别又在哪呢?

1.COUNT(*)函数

COUNT(*)函数返回由SELECT语句返回的结果集中的行数。COUNT(*)函数计算包含NULL和非NULL值的行,即:所有行。

如果使用COUNT(*)函数对表中的数字行进行计数,而不使用WHERE子句选择其他列,则其执行速度非常快。

这种优化仅适用于MyISAM表,因为MyISAM表的行数存储在information_schema数据库的tables表的table_rows列中; 因此,MySQL可以很快地检索它。

2.COUNT(常量)函数

介绍完了COUNT(*),接下来看看COUNT(1),对于,这二者到底有没有区别,网上的说法众说纷纭。

有的说COUNT(*)执行时会转换成COUNT(1),所以COUNT(1)少了转换步骤,所以更快。

还有的说,因为MySQL针对COUNT(*)做了特殊优化,所以COUNT(*)更快。

那么,到底哪种说法是对的呢?看下MySQL官方文档是怎么说的:

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

画重点:same way , no performance difference。所以,对于COUNT(1)和COUNT(*),MySQL的优化是完全一样的,根本不存在谁比谁快!

那既然COUNT(*)和COUNT(1)一样,建议用哪个呢?

建议使用COUNT(*)!因为这个是SQL92定义的标准统计行数的语法,而且本文只是基于MySQL做了分析,关于Oracle中的这个问题,也是众说纷纭的呢。

3.COUNT(列名)

MySQL COUNT(column)返回不包含NULL值的所有行数。

4.COUNT(expression)

COUNT(expression)返回不包含NULL值的行数,expression 是表达式。

5.示例

下面来看一下例子:

原表数据:

(1) count(*)

SELECT COUNT(*) count FROM t_iov_help_feedback

结果:

可以看到,只要表中存在记录,不管是否有字段为NULL,都会被count出来,查询出来数量为4(全部)。

(2) count(1)

SELECT COUNT(1) count FROM t_iov_help_feedback

结果:

可以看到,count(1)这个用法和count(*)的结果是一样的,查询出来数量为4(全部)。

(3) count(column)

SELECT COUNT(UPLOAD_PICTURES) count FROM t_iov_help_feedback;

结果:

可以看到,将UPLOAD_PICTURES 字段为 NULL 的记录不计算在内,有1条记录为NULL,所以查询出来数量为3。

注:

如果表没有主键,那么count(1)比count(*)快; 如果有主键,那么count(主键,联合主键)比count(*)快; 如果表只有一个字段,count(*)最快。count(1)跟count(主键)一样,只扫描主键。count(*)跟count(非主键)一样,扫描整个表。明显前者更快一些。

性能问题:

任何情况下SELECT COUNT(*) FROM tablename是最优选择,(指没有where的情况);尽量减少SELECT COUNT(*) FROM tablename WHERE COL = 'value' 这种查询;杜绝SELECT COUNT(COL) FROM tablename WHERE COL2 = 'value' 的出现。COUNT(*)的优化

前面提到了COUNT(*)是SQL92定义的标准统计行数的语法,所以MySQL数据库对他进行过很多优化。那么,具体都做过哪些事情呢?

这里的介绍要区分不同的执行引擎。MySQL中比较常用的执行引擎就是InnoDB和MyISAM。

MyISAM和InnoDB有很多区别,其中有一个关键的区别和我们接下来要介绍的COUNT(*)有关,那就是MyISAM不支持事务,MyISAM中的锁是表级锁;而InnoDB支持事务,并且支持行级锁。

因为MyISAM的锁是表级锁,所以同一张表上面的操作需要串行进行,所以,MyISAM做了一个简单的优化,那就是它可以把表的总行数单独记录下来,如果从一张表中使用COUNT(*)进行查询的时候,可以直接返回这个记录下来的数值就可以了,当然,前提是不能有where条件。

MyISAM之所以可以把表中的总行数记录下来供COUNT(*)查询使用,那是因为MyISAM数据库是表级锁,不会有并发的数据库行数修改,所以查询得到的行数是准确的。

但是,对于InnoDB来说,就不能做这种缓存操作了,因为InnoDB支持事务,其中大部分操作都是行级锁,所以可能表的行数可能会被并发修改,那么缓存记录下来的总行数就不准确了。

但是,InnoDB还是针对COUNT(*)语句做了些优化的。

在InnoDB中,使用COUNT(*)查询行数的时候,不可避免的要进行扫表了,那么,就可以在扫表过程中下功夫来优化效率了。

从MySQL 8.0.13开始,针对InnoDB的SELECT COUNT(*) FROM tbl_name语句,确实在扫表的过程中做了一些优化。前提是查询语句中不包含WHERE或GROUP BY等条件。

我们知道,COUNT(*)的目的只是为了统计总行数,所以,他根本不关心自己查到的具体值,所以,他如果能够在扫表的过程中,选择一个成本较低的索引进行的话,那就可以大大节省时间。

我们知道,InnoDB中索引分为聚簇索引(主键索引)和非聚簇索引(非主键索引),聚簇索引的叶子节点中保存的是整行记录,而非聚簇索引的叶子节点中保存的是该行记录的主键的值。

所以,相比之下,非聚簇索引要比聚簇索引小很多,所以MySQL会优先选择最小的非聚簇索引来扫表。所以,当我们建表的时候,除了主键索引以外,创建一个非主键索引还是有必要的。

至此,我们介绍完了MySQL数据库对于COUNT(*)的优化,这些优化的前提都是查询语句中不包含WHERE以及GROUP BY条件。

总结

本文介绍了COUNT函数的用法,主要用于统计表行数。主要语法包含COUNT(*)、COUNT(字段)和COUNT(1)。

因为COUNT(*)是SQL92定义的标准统计行数的语法,所以MySQL对他进行了很多优化,MyISAM中会直接把表的总行数单独记录下来供COUNT(*)查询,而InnoDB则会在扫表的时候选择最小的索引来降低成本。当然,这些优化的前提都是没有进行where和group的条件查询。

在InnoDB中COUNT(*)和COUNT(1)实现上没有区别,而且效率一样,但是COUNT(字段)需要进行字段的非NULL判断,所以效率会低一些。

因为COUNT(*)是SQL92定义的标准统计行数的语法,并且效率高,所以请直接使用COUNT(*)查询表的行数!

that's all,ok...

标签: #mysql的count