ITPub博客

首页 > 数据库 > MySQL > MySQL的COUNT语句--count(*)、 count(常量)、 count(列名)

MySQL的COUNT语句--count(*)、 count(常量)、 count(列名)

原创 MySQL 作者:lhrbest 时间:2019-10-21 10:17:19 0 删除 编辑

MySQL的COUNT语句--count(*)、 count(常量)、 count(列名)

数据库查询相信很多人都不陌生,所有经常有人调侃程序员就是CRUD专员,这所谓的CRUD指的就是数据库的增删改查。

在数据库的增删改查操作中,使用最频繁的就是查询操作。 而在所有查询操作中,统计数量操作更是经常被用到。

关于数据库中行数统计,无论是MySQL还是Oracle,都有一个函数可以使用,那就是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(字段名)的执行过程是怎样的?

以上10道题,如果您可以全部准确无误的回答的话,那说明你真的很了解COUNT函数了,如果有哪些知识点是不了解的,那么本文正好可以帮你答疑解惑。

1、认识COUNT

关于COUNT函数,在MySQL官网中有详细介绍:

简单翻译一下:

1、COUNT(expr) ,返回SELECT语句检索的行中expr的值不为NULL的数量。 结果是一个BIGINT值。

2、如果查询结果没有命中任何记录,则返回0

3、但是,值得注意的是, COUNT(*)  的统计结果中,会包含值为NULL的行数。

create table t_count(id int,id2 int);
insert into t_count values(null,null);
insert into t_count values(1,null);
insert into t_count values(null,1);
insert into t_count values(1,null);
insert into t_count values(null,1);
insert into t_count values(1,null);
insert into t_count values(null,null);
select count(*),count(id),count(id2),count(1) from t_count;


即以下表记录

root@localhost[lhrdb]> create table t_count(id int,id2 int);

Query OK, 0 rows affected (0.36 sec)


root@localhost[lhrdb]> 

root@localhost[lhrdb]> insert into t_count values(null,null);

Query OK, 1 row affected (0.07 sec)


root@localhost[lhrdb]> insert into t_count values(1,null);

Query OK, 1 row affected (0.06 sec)


root@localhost[lhrdb]> insert into t_count values(null,1);

Query OK, 1 row affected (0.08 sec)


root@localhost[lhrdb]> insert into t_count values(1,null);

Query OK, 1 row affected (0.03 sec)


root@localhost[lhrdb]> insert into t_count values(null,1);

Query OK, 1 row affected (0.05 sec)


root@localhost[lhrdb]> insert into t_count values(1,null);

Query OK, 1 row affected (0.03 sec)


root@localhost[lhrdb]> insert into t_count values(null,null);

Query OK, 1 row affected (0.08 sec)


root@localhost[lhrdb]> 

root@localhost[lhrdb]> select * from t_count;

+------+------+

| id   | id2  |

+------+------+

| NULL | NULL |

|    1 | NULL |

| NULL |    1 |

|    1 | NULL |

| NULL |    1 |

|    1 | NULL |

| NULL | NULL |

+------+------+

7 rows in set (0.00 sec)



使用语句count(*),count(id),count(id2)查询结果如下:

root@localhost[lhrdb]> select count(*),count(id),count(id2),count(1),count(2) from t_count;

+----------+-----------+------------+----------+----------+

| count(*) | count(id) | count(id2) | count(1) | count(2) |

+----------+-----------+------------+----------+----------+

|        7 |         3 |          2 |        7 |        7 |

+----------+-----------+------------+----------+----------+

1 row in set (0.00 sec)



除了 COUNT(id) COUNT(*) 以外,还可以使用 COUNT(常量) (如 COUNT(1) )来统计行数,那么这三条SQL语句有什么区别呢? 到底哪种效率更高呢? 为什么《阿里巴巴Java开发手册》中强制要求不让使用  COUNT(列名) 或  COUNT(常量) 来替代  COUNT(*) 呢?



COUNT(列名)、COUNT(常量)和COUNT(*)之间的区别

前面我们提到过 COUNT(expr) 用于做行数统计,统计的是expr不为NULL的行数,那么 COUNT(列名) 、  COUNT(常量)  和  COUNT(*) 这三种语法中,expr分别是 列名 、  常量  和 *

那么 列名 、  常量  和  * 这三个条件中, 常量  是一个固定值,肯定不为NULL。 * 可以理解为查询整行,所以肯定也不为NULL,那么就只有 列名 的查询结果有可能是NULL了。

所以,  COUNT(常量) 和  COUNT(*)表示的是直接查询符合条件的数据库表的行数。而 COUNT(列名)表示的是查询符合条件的列的值不为NULL的行数。

除了查询得到结果集有区别之外, COUNT(*) 相比 COUNT(常量)  和  COUNT(列名) 来讲, COUNT(*)是SQL92定义的标准统计行数的语法,因为他是标准语法,所以MySQL数据库对他进行过很多优化。

SQL92,是数据库的一个ANSI/ISO标准。它定义了一种语言(SQL)以及数据库的行为(事务、隔离级别等)。


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(1)

介绍完了 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中的这个问题,也是众说纷纭的呢。


COUNT(字段)

最后,就是我们一直还没提到的COUNT(字段),他的查询就比较简单粗暴了,就是进行全表扫描,然后判断指定字段的值是不是为NULL,不为NULL则累加。

相比 COUNT(*) COUNT(字段) 多了一个步骤就是判断所查询的字段是否为NULL,所以他的性能要比 COUNT(*) 慢。


总结

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

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

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

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

参考资料:

https://dev.mysql.com/doc/refman/8.0/en/group-by-functions.html#function_count






About Me

........................................................................................................................

● 本文作者:小麦苗,部分内容整理自网络,若有侵权请联系小麦苗删除

● 本文在itpub、博客园、CSDN和个人微 信公众号( xiaomaimiaolhr)上有同步更新

● 本文itpub地址: http://blog.itpub.net/26736162

● 本文博客园地址: http://www.cnblogs.com/lhrbest

● 本文CSDN地址: https://blog.csdn.net/lihuarongaini

● 本文pdf版、个人简介及小麦苗云盘地址: http://blog.itpub.net/26736162/viewspace-1624453/

● 数据库笔试面试题库及解答: http://blog.itpub.net/26736162/viewspace-2134706/

● DBA宝典今日头条号地址: http://www.toutiao.com/c/user/6401772890/#mid=1564638659405826

........................................................................................................................

● QQ群号: 230161599 、618766405

● 微 信群:可加我微 信,我拉大家进群,非诚勿扰

● 联系我请加QQ好友 646634621 ,注明添加缘由

● 于 2019-10-01 06:00 ~ 2019-10-31 24:00 在西安完成

● 最新修改时间:2019-10-01 06:00 ~ 2019-10-31 24:00

● 文章内容来源于小麦苗的学习笔记,部分整理自网络,若有侵权或不当之处还请谅解

● 版权所有,欢迎分享本文,转载请保留出处

........................................................................................................................

小麦苗的微店https://weidian.com/s/793741433?wfr=c&ifr=shopdetail

小麦苗出版的数据库类丛书http://blog.itpub.net/26736162/viewspace-2142121/

小麦苗OCP、OCM、高可用网络班http://blog.itpub.net/26736162/viewspace-2148098/

小麦苗腾讯课堂主页https://lhr.ke.qq.com/

........................................................................................................................

使用 微 信客户端扫描下面的二维码来关注小麦苗的微 信公众号( xiaomaimiaolhr)及QQ群(DBA宝典)、添加小麦苗微 信, 学习最实用的数据库技术。

........................................................................................................................

欢迎与我联系

 

 



来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/26736162/viewspace-2660733/,如需转载,请注明出处,否则将追究法律责任。

下一篇: 没有了~
请登录后发表评论 登录
全部评论
QQ:646634621| 网名:小麦苗| 微信公众号:xiaomaimiaolhr| 11g OCM| QQ群:618766405 微信群:私聊| 《数据库笔试面试宝典》作者| OCP、OCM、高可用(RAC+DG+OGG)网络班开讲啦,有需要的小伙伴可以私聊我。

注册时间:2012-09-23

  • 博文量
    1353
  • 访问量
    8205650