COUNT函数暗藏玄机

1.前言

在数据库操作中统计操作经常用到。
关于数据库中行数统计,大家一定会想到COUNT函数,但这个函数却暗藏着玄机。

2.常见问题

  • COUNT有几种用法 ?
  • COUNT(字段名)和COUNT(*)的查询结果有什么不同 ?
  • COUNT(1)和COUNT(*)之间有什么不同 ?
  • COUNT(1)和COUNT(*)之间的效率哪个更高 ?
  • 为什么《阿里巴巴Java开发手册》建议使用COUNT(*) ?

3.函数介绍

3.1 初识COUNT

关于COUNT函数,MySQL官网中是这样介绍的:
image.png
简译:

  • COUNT(expr) ,返回SELECT语句检索的行中expr的值不为NULL的数量。结果是一个BIGINT值。
  • 如果查询结果没有命中任何记录,则返回0
  • 但是,值得注意的是,COUNT(*) 的统计结果中,会包含值为NULL的行数。

3.2 测试一下

3.2.1 创建测试表

CREATE TABLE `count_test` (
  `id` int(10) NOT NULL AUTO_INCREMENT COMMENT '自增ID',
  `id_one` int(10) DEFAULT NULL COMMENT 'ID1',
  `id_two` int(10) DEFAULT NULL COMMENT 'ID2',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='count 测试表';

3.2.2 初始化测试数据

INSERT INTO `count_test`
(`id`, `id_one`, `id_two`) 
VALUES 
(1, NULL, 1),
(2, NULL, 1),
(3, 1, NULL),
(4, 1, 1),
(5, NULL, 1);

使用count(*),count(id),count(id_one),count(id_two):

SELECT
	count( * ),
	count( id ),
	count( id_one ),
	count( id_two ) 
FROM
	count_test;

3.2.2 测试结果与结论

image.png
count(*) 查询的是表中所有总行数 ;
count(id) 查询的是表中id不为空的记录总行数 ;
count(id_one) 查询的是表中id_one不为空的记录总行数 ;
count(id_two) 查询的是表中id_two不为空的记录总行数 ;

3.3 用法间的区别

3.3.1 COUNT有几种用法 ?

  • COUNT(expr)用于做行数统计,统计的是expr不为NULL的行数 ;
  • COUNT(常量) 和 COUNT(*)表示的是直接查询符合条件的数据库表的行数 ;
  • COUNT(列名)表示的是查询符合条件的列的值不为NULL的行数 ;

3.3.2 COUNT(*)与COUNT(常量)

COUNT(*)与COUNT(常量)

  • COUNT(1)和COUNT(*)之间有什么不同?
  • COUNT(1)和COUNT(*)之间的效率哪个更高?
  • 为什么《阿里巴巴Java开发手册》建议使用COUNT(*)

image.png

3.3.3 COUNT(*)和COUNT(1)

众说纷纭

  • 有的说COUNT(*)执行时会转换成COUNT(1),所以COUNT(1)少了转换步骤,所以更快。
  • 还有的说,因为MySQL针对COUNT()做了特殊优化,所以COUNT()更快。

那么,到底哪种说法是对的呢?
MySQL官方文档
image.png

简译重点:

  • 在InnoDB引擎下 same way , no performance difference ;

3.3.4 其它用法

1.去重统计
COUNT(DISTINCT expr,[expr…])

SELECT count( DISTINCT id_one ) FROM count_test;

4.扩展篇

MySQL中常用的执行引擎就是InnoDB和MyISAM,count函数在InnoDB和MyISAM引擎下的区别?
常见问题

  • MySQL的MyISAM引擎对COUNT(*)做了哪些优化?
  • MySQL的InnoDB引擎对COUNT(*)做了哪些优化?
  • MySQL对COUNT(*)做的优化,有一个关键的前提是什么?

MyISAM和InnoDB有很多区别

  • MyISAM不支持事务;
  • MyISAM中的锁是表级锁;
  • InnoDB支持事务,并且支持行级锁,本文主要讲count函数的区别。


MyISAM引擎下count

  • 因为MyISAM引擎下的锁是表级锁,所以同一张表的操作是串行的,MyISAM做了一个简单的优化,把表的总行数单独记录下来,如果从一张表中使用COUNT(*)进行查询的时候,可以直接返回这个记录下来的总数,当然,前提是不能有where条件
  • MyISAM可以把表中的总行数记录下来供COUNT(*)查询使用,那是因为MyISAM数据库是表级锁,不会有并发的数据库行数修改,所以查询得到的行数是准确的。


InnoDB引擎下的count
但是,对于InnoDB来说,就不能做这种缓存操作了,因为InnoDB支持事务,其中大部分操作都是行级锁,所以可能表的行数可能会被并发修改,那么缓存记录下来的总行数就不准确了。
但是,在InnoDB中,使用COUNT()查询行数的时候,不可避免的要进行扫表了,InnoDB还是针对COUNT()语句做了些优化的。说白了就是索引


InnoDB引擎下的count优化
MySQ对COUNT语句,做了一部分优化。但前提是查询语句中不包含WHERE或GROUP BY等条 ;

索引提速
COUNT(*)的目的只是为了统计总行,所以,如果能够在扫表的过程中,选择一个成本较低的索引进行的话,那就可以大大节省时间 。

InnoDB中索引分为聚簇索引(主键索引)和非聚簇索引(非主键索引),聚簇索引的叶子节点中保存的是整行记录,而非聚簇索引的叶子节点中保存的是该行记录的主键的值。
所以,相比之下,非聚簇索引要比聚簇索引小很多,所以MySQL会优先选择最小的非聚簇索引来扫表。所以,当我们建表的时候,除了主键索引以外,创建一个非主键索引还是有必要的。

参考资料:官网介绍传送门

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员小强

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值