MySQL中count(field),count(*),count(常量)的区别

一 概要

最近在研究mybatis的时候发现**Mapper.xml在编写SQL的时候发现聚合函数(count())使用起来挺方便,count() 是一个聚合函数,对于返回的结果集,一行行地判断,如果 count 函数的参数不是 NULL,累计值就加 1,否则不加,最后返回累计值。

<select id="checkEmail" parameterType="string" resultType="int">
    select count(*) from mark_user
    where email = #{email}
</select>

此处使用的是count(*),而且在阿里巴巴开发手册中强制要求使用count(*),原文如下:

count(*),count(常量),count(field)的概述:

count(*) 和 count(常量)直接查询符合条件的数据库表的行数,而count(field)则会查询符合NOT NULL的数据库行数。此外count(*)时SQL92定义的标准统计行数的语法,而且MySQL数据库对其进行过很多优化。

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

二 count(*),count(常量),count(field)的区别

执行条件执行方法MySQL中执行的结果不同count方法的总结
存在where语句count(*)count(常量)如(count(1)) 和 count(*)表示的是直接查询符合条件的数据库的行数,而 count(field)(field的可以理解为表的列名)表示的是查询符合条件的列的值不为NULL的行数。
count(1)
count(field)
当数据库中某指定字段NULL时,如上图中phone字段的第5条数据为NULLcount(*)

对于 count(1) 来说:InnoDB 引擎遍历整张表,但不取值。对于返回的每一行,放一个数字“1”进去,判断是不可能为空的,按行累加。

对于 count(field) 来说:

1.如果这个字段定义为NOT NULL的时候,一行一行地从记录里面读取这个字段,判断不能为NOT NULL,按行累加;

2.如果这个字段允许为NULL的时候,在执行存在NULL的情况,这时候判断不是NULL的时候才会累加。

但是 count(*) 是例外,并不会把全部字段取出来,而是专门做了优化,不取值。count(*) 肯定不是 null,按行累加。

count(field)
count(1)

MySQL官方文档:InnoDB handles SELECT COUNT(*) and SELECT COUNT(1) operations in the same way. There is no performance difference.

对于count(1)count(*),MySQL的优化是完全一样的,根本不存在谁更快!但依旧建议使用count(*),因为这是SQL92定义的标准统计行数的语法。

而count(field)进行全表扫描,判断指定字段的值是否为NULL,不为NULL则累加。性能比count(1)count(*)慢。所以,按照函数的执行效率排序为可以认为结果为: count(field) < count(主键 id) < count(1) ≈ count(*)。

三 count(*)的优化

MySQL中两种主要执行引擎:

1. InnoDB引擎:InnoDB支持事务,大部分操作行级锁,行可能被并行修改,那么缓存记录不准确。而且InnoDB针对count(*)通过低成本的索引进行扫表,而不关注表的具体内容。InnoDB中索引分为聚簇索引(主键索引)和非聚簇索引(非主键索引),聚簇索引的叶子节点中保存的时整行数据,而非聚簇索引的叶子节点保存到时该行记录的主键值。同时MySQL会优先选择最小的非聚簇索引来扫表。这些优化的前提是查询语句中不包含where条件和group by 条件。

2. MyISAM引擎:MyISAM做了一个简单的优化,把表的总行数单独记录下来,如执行count(*)时可以直接返回,前提时不能有where条件的。MyISAM时表级锁,不会有并发的行操作,所以查到的结果时准确的。

MyISAM不支持事务,MyISAM中的锁是表级锁,而InnoDB支持事务,并且支持行级锁。

四 总结

        聚合函数count()主要是用于统计表行数,主要用法有count(*),count(1),count(主键id)和count(field);

其中count(*)是SQL92定义的标准统计行数的语法,MySQL对其进行了很多优化,MyISAM中会直接把表的总行数单独记录下提供给count(*),而InnoDB则会在扫表的时候选择最小的索引(非聚簇索引,其叶子节点保存的是主键值)来降低成本,前提是,查询语句中不能够存在where和group条件。

在InnoDB中,count(*) 和 count(1)实现上没有区别,效率等同,当时count(field)则需要对字段进行非UNLL判断,所以效率会有所下降。

综上所述可得为什么阿里巴巴开发手册上强制规定不能够用count(列名)和count(常量)来代替count(*);

对数据量为千万的表进行这三种查询:

 根据结果可知,count(*)和count(1)的所耗时间最少,且接近,而count(field)所消耗的时间最多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值