MySQL查询优化之COUNT()

COUNT()聚合函数,以及如何优化使用了该函数的查询,极可能是MySQL中最容易被误解的前10个话题之一,在网上随便搜索一下就能看到不少错误的理解,可能比咱们想象的多得多。mysql

在作优化以前,先来看看COUNT()函数的真正做用是什么。web

COUNT()的做用

COUNT()是一个特殊的函数,有两种很是不一样的做用:它能够统计某个列值的数量,也能够统计行数。在统计列值时要求列值时非空的(不统计NULL)。若是在COUNT()的括号中指定了列或列的表达式,统计的就是这个表达式有值的结果数。由于不少人对NULL理解有问题,因此这里很容易产生误解。若是想了解更多关于SQL语句中NULL的含义,建议阅读一些关于SQL语句基础的书籍。(关于这个话题,互联网上的一些信息是不够精确的)sql

COUNT()的另一个做用是统计结果集的行数。当MySQL确认括号内的表达式值不可能为空时,实际上就是在统计行数。最简单的就是当咱们使用COUNT(*)的时候,这种状况下通配符*并不会像咱们猜测的那样扩展成全部的列,实际上,它会忽略全部的列而直接统计全部的行数。数据库

咱们发现一个最多见的错误就是,在括号内指定了一个列却但愿统计结果集的行数。若是但愿知道的是结果集的行数,最好使用COUNT(*),这样写意义清晰,性能也会很好。svg

关于MyISAM的神话

一个容易产生的误解就是:MyISAM的COUNT()函数老是很是快,不过这是有前提条件的,即只有没有任何where条件的COUNT(*)才很是快,由于此时无需实际地去计算表的行数。MySQL能够利用存储引擎的特性直接得到这个值。若是MySQL知道某列col不可能为NULL值,那么MySQL内部会将COUNT(col)表达式优化为COUNT(*)。函数

当统计带WHERE子句的结果集行数,能够是统计某个列值的数量时,MySQL的COUNT()和其它存储引擎没有任何不一样,就再也不有神话般的速度了。因此在MyISAM引擎表上执行COUNT()有时候比别的引擎快,有时候比别的引擎慢,这受不少因素影响,要视具体状况而定。性能

简单的优化

有时候可使用MyISAM在COUNT()全表很是快的这个特性,来加速一些特定条件COUNT()的查询。在下面的例子中,咱们使用标准数据库world来看看如何快速查找到全部ID大于5的城市。能够像下面这样来写这个查询:优化

mysql>SELECT COUNT(*) FROM world.city WHERE ID > 5;

经过SHOW STATUS的结果能够看到该查询须要扫描4097行数据。若是将条件反转一下,先查找ID小于等于5的城市数,而后用总城市数一减就能获得一样的结果,却能够将扫描的行数减小到5行之内:spa

mysql>SELECT (SELECT COUNT(*) FROM world.city) - COUNT(*) FROM world.city WHERE ID <= 5;

这样作能够大大减小须要扫描的行数,是由于在查询优化阶段会将其中的子查询直接当成一个常数来处理,咱们能够经过EXPLAIN来验证这点。code

在邮件组和IRC聊天频道中,一般会看到这样的问题:若是在同一个查询中统计同一个列的不一样值的数量,以减小查询的语句量。例如,假设可能须要经过一个查询返回各类不一样颜色的商品数量,此时不能使用OR语句,由于这样作就没法区分不一样颜色的商品数量。下面的查询能够在必定程度上解决这个问题。

mysql>SELECT SUM(IF(color = 'blue', 1, 0)) AS blue,SUM(IF(color = 'red', 1, 0)) AS red FROM items;

也可使用COUNT()而不是SUM()实现一样的目的,只须要将知足条件设置为真,不知足条件设置为NULL便可:

mysql>SELECT COUNT(color = 'blue' OR NULL) AS blue,COUNT(color = 'red' OR NULL) AS red FROM items;

本文内容转载自《高性能MySQL第三版-第6.7.1章节》