1.前言
在数据库操作中统计操作经常用到。
关于数据库中行数统计,大家一定会想到COUNT函数,但这个函数却暗藏着玄机。
2.常见问题
- COUNT有几种用法 ?
- COUNT(字段名)和COUNT(*)的查询结果有什么不同 ?
- COUNT(1)和COUNT(*)之间有什么不同 ?
- COUNT(1)和COUNT(*)之间的效率哪个更高 ?
- 为什么《阿里巴巴Java开发手册》建议使用COUNT(*) ?
3.函数介绍
3.1 初识COUNT
关于COUNT函数,MySQL官网中是这样介绍的:
简译:
- 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 测试结果与结论
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(*)
3.3.3 COUNT(*)和COUNT(1)
众说纷纭
- 有的说COUNT(*)执行时会转换成COUNT(1),所以COUNT(1)少了转换步骤,所以更快。
- 还有的说,因为MySQL针对COUNT()做了特殊优化,所以COUNT()更快。
那么,到底哪种说法是对的呢?
MySQL官方文档
简译重点:
- 在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会优先选择最小的非聚簇索引来扫表。所以,当我们建表的时候,除了主键索引以外,创建一个非主键索引还是有必要的。
参考资料:官网介绍传送门