网站首页 > 技术文章 正文
在我们的日常开发工作中,数据库的增删改查操作可能是最基础也是使用最广泛的;数据库查询是数据基本操作中必不可少的,而在查询数据时首先会返回数据数量,也就是我们这节要讨论的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...
猜你喜欢
- 2024-11-11 Oracle OAC数据分析系列:设置数值格式和货币符号
- 2024-11-11 Oracle 数据库之SQL分组函数 sql的分组函数
- 2024-11-11 Oracle表访问方法-全表扫描 oracle访问数据的存取方法
- 2024-11-11 Oracle数据立方体多维数据用CUBE处理
你 发表评论:
欢迎- 615℃几个Oracle空值处理函数 oracle处理null值的函数
- 608℃Oracle分析函数之Lag和Lead()使用
- 595℃0497-如何将Kerberos的CDH6.1从Oracle JDK 1.8迁移至OpenJDK 1.8
- 592℃Oracle数据库的单、多行函数 oracle执行多个sql语句
- 587℃Oracle 12c PDB迁移(一) oracle迁移到oceanbase
- 580℃【数据统计分析】详解Oracle分组函数之CUBE
- 569℃最佳实践 | 提效 47 倍,制造业生产 Oracle 迁移替换
- 560℃Oracle有哪些常见的函数? oracle中常用的函数
- 最近发表
-
- PageHelper - 最方便的 MyBatis 分页插件
- 面试二:pagehelper是怎么实现分页的,
- MyBatis如何实现分页查询?(mybatis-plus分页查询)
- SpringBoot 各种分页查询方式详解(全网最全)
- 如何在Linux上运行exe文件,怎么用linux运行windows软件
- 快速了解hive(快速了解美国50个州)
- Python 中的 pyodbc 库(pydbclib)
- Linux搭建Weblogic集群(linux weblogic部署项目步骤)
- 「DM专栏」DMDSC共享集群之部署(一)——共享存储配置
- 故障分析 | MySQL 派生表优化(mysql pipe)
- 标签列表
-
- 前端设计模式 (75)
- 前端性能优化 (51)
- 前端模板 (66)
- 前端跨域 (52)
- 前端缓存 (63)
- 前端aes加密 (58)
- 前端脚手架 (56)
- 前端md5加密 (54)
- 前端路由 (61)
- 前端数组 (73)
- 前端js面试题 (50)
- 前端定时器 (59)
- 前端获取当前时间 (50)
- Oracle RAC (76)
- oracle恢复 (77)
- oracle 删除表 (52)
- oracle 用户名 (80)
- oracle 工具 (55)
- oracle 内存 (55)
- oracle 导出表 (62)
- oracle约束 (54)
- oracle 中文 (51)
- oracle链接 (54)
- oracle的函数 (58)
- 前端调试 (52)
本文暂时没有评论,来添加一个吧(●'◡'●)