数据库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、如果查询结果没有命中任何记录,则返回03、但是,值得注意的是,COUNT(*)
的统计结果中,会包含值为NULL的行数。即以下表记录
create table #bla(id int,id2 int)
insert #bla values(null,null)
insert #bla values(1,null)
insert #bla values(null,1)
insert #bla values(1,null)
insert #bla values(null,1)
insert #bla values(1,null)
insert #bla values(null,null)
使用语句count(*),count(id),count(id2)查询结果如下:
select count(*),count(id),count(id2)
from #bla
results 7 3 2
除了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
数据库Count 语句详解的更多相关文章
- mysql基础篇 - SELECT 语句详解
基础篇 - SELECT 语句详解 SELECT语句详解 一.实验简介 SQL 中最常用的 SELECT 语句,用来在表中选取数据,本节实验中将通过一系列的动手操作详细学习 SELEC ...
- SqlServer数据库性能优化详解
数据库性能优化详解 性能调节的目的是通过将网络流通.磁盘 I/O 和 CPU 时间减到最小,使每个查询的响应时间最短并最大限度地提高整个数据库服务器的吞吐量.为达到此目的,需要了解应用程序的需求和数据 ...
- mysql 聚集函数 count 使用详解
mysql 聚集函数 count 使用详解 本文将探讨以下问题 1.count(*) . count(n).count(null)与count(fieldName) 2.distinct 与 coun ...
- Net Core中数据库事务隔离详解——以Dapper和Mysql为例
Net Core中数据库事务隔离详解--以Dapper和Mysql为例 事务隔离级别 准备工作 Read uncommitted 读未提交 Read committed 读取提交内容 Repeatab ...
- 【转】MySQL用户管理及SQL语句详解
[转]MySQL用户管理及SQL语句详解 1.1 MySQL用户管理 1.1.1 用户的定义 用户名+主机域 mysql> select user,host,password from mysq ...
- 重新学习MySQL数据库7:详解MyIsam与InnoDB引擎的锁实现
重新学习Mysql数据库7:详解MyIsam与InnoDB引擎的锁实现 说到锁机制之前,先来看看Mysql的存储引擎,毕竟不同的引擎的锁机制也随着不同. 三类常见引擎: MyIsam :不支持事务,不 ...
- MySQL之SELECT 语句详解
本文参考实验楼的SELECT 语句详解结合自己操作部分而写成. 注意:大多数系统中,SQL语句都是不区分大小写的,但是出于严谨和便于区分保留字和变量名,在书写的时,保留字应大写,而变量名应小写.所谓的 ...
- 问题:oracle select into;结果:oracle SELECT INTO 和 INSERT INTO SELECT 两种表复制语句详解
oracle SELECT INTO 和 INSERT INTO SELECT 两种表复制语句详解 (2011-07-08 08:59:47) 转载▼ 标签: it 分类: oracle 我们经常会遇 ...
- 数据库开发-pymysql详解
数据库开发-pymysql详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Python支持的MySQL驱动 1>.什么是驱动 与MySQL通信就是典型的CS模式.Se ...
随机推荐
- quartz2.3.0(十二)通过RMI协议向Scheduler调度器远程添加job任务
此代码示例通过RMI协议向Scheduler调度器远程添加job任务. 代码文件包括:job任务类(SimpleJob.java).RMI服务端server类(RemoteServerExample. ...
- golang ---获取磁盘信息
package main import ( "fmt" "github.com/StackExchange/wmi" ) type Storage struct ...
- node-red File读取好保存
File节点是操作文件的节点 file文件的保存 拖拽 注入节点inject file节点(writes msg.payload to a file)和 debug节点到工作区,并连线 设置file ...
- Redis之RDB和AOF持久化介绍
什么是数据库状态 redis是一个键值对的数据库服务器,服务器中通常包含中任意个非空的数据库,而每个数据库又可以包含任意个键值对,为了方便起见,我们将服务器中的非空数据库以及他们的键值对统称为数据库状 ...
- 【洛谷 SP8093】 JZPGYZ - Sevenk Love Oimaster(后缀自动机)
题目链接 广义sam.. #include <cstdio> #include <cstring> #include <algorithm> using names ...
- JAVA基础之Servlet
个人理解: servlet是用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容.需要注意的在创建的服务器端的目录和实际上存在差别的,并且访 ...
- sql server 获取某一字段分组数据的前十条记录
1.sql 语法 select m, n from ( select row_number () over (partition by m order by n desc) rn,--以m分组,分组内 ...
- 使用gulp构建项目
gulp.js作为一个前端构建工具,类似于webpack.Grountjs.rollupjs,不过相对于其他几种打包工具,gulp的使用更轻量,配置更简单,打包速度更快,今天不说他们几个的区别,也不说 ...
- [LeetCode] 63. 不同路径 II ☆☆☆(动态规划)
描述 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” ). 机器人每次只能向下或者向右移动一步.机器人试图达到网格的右下角(在下图中标记为“Finish”). 现在 ...
- mysql遇到时区问题的坑(Java解决方案)
最近项目遇到一个坑,就是server和db之间存在时区问题,本人的db是utc时间, 可以使用代码设置时区来解决,本人这里使用joda三方包,joda蛮好用的,具体用法这里不做详细描述. 先引入pom ...