原文地址:https://dev.mysql.com/doc/refman/5.7/en/where-optimization.html
译文:
8.2.1.1 where语句的优化
这一部分讨论用于where语句的优化问题。示例采用select语句,但是同样的优化也适用于delete和update语句中的where字句。
Note:
因为在MySQL优化程序方面的工作是不断推进的,所以此处记录的MySQL优化并不完整。
你也许尝试过在牺牲SQL语句可读性的条件下,重写你的查询语句从而让SQL语句运行的更快。因为MySQL自动地做了类似的优化,所以你可以经常避免这类工作,确保你的查询语句更容易理解以及更容易维护。下面是一些MySQL优化:
去除没有必要的括号:
((a AND b) AND c OR (((a AND b) AND (c AND d))))
-> (a AND b AND c) OR (a AND b AND c AND d)
常量合并:
(a<b AND b=c) AND a=5
-> b>5 AND b=c AND a=5
常量条件移除(因为常量合并而需要):
(B>=5 AND B=5) OR (B=6 AND 5=5) OR (B=7 AND 5=6)
-> B=5 OR B=6
索引用的常量表达式仅被评估一次。
对于myisam引擎表和memory引擎表来说,作用于单表且没有where子句的count(*),会被直接从表信息中检索出来。这同样适用于仅作用于一个单表的任何非空表达式。
先检测一些无效的常量表达式,然后MySQL快速检测一些不可能和不返回数据的select语句。
如果你不使用group by或聚合函数的话(如count(),min()等),having与where等同。
对于连接中的每一个表来说,使用一个更简单的where语句是为了在每个表上更快的执行,以及尽可能多的跳过表中的行。
查询中的常量表比其他表都要先被读取。下面的任何表都属于常量表:
-
1)空表或只有一行的表
-
2)与含有主键索引或者唯一索引的where语句一起使用的表,表中的所有索引都被比作是常量表达式并且为非空类型
下面的表都可以用作常量表:
SELECT * FROM t WHERE primary_key=1;
SELECT * FROM t1,t2 WHERE t1.primary_key=1 AND t2.primary_key=t1.id;
用于连接表的最好的连接方式通常只有尝试了所有可能后才会找到。如果order by和group by子句中的列都来源于同一张表,这张表就是用于连接的首选表。
如果连接查询中有一个order by语句和一个group by语句,或者是如果order by或group by语句中包含来自不是第一张表的表,这种情况下会产生一个临时表。
如果你使用SQL_SMALL_RESULT
修饰符, MySQL 会使用一个内存中的临时表。
查询每个表索引,并使用最佳索引,除非优化器认为使用表扫描更有效。 曾经,是否使用扫描取决于最佳索引是否跨越了超过30%的表,但是现在一个固定的百分数不再是是使用扫描还是使用索引的决定条件。优化器现在更加复杂,它的估计基于其他因素,如表大小、行数和I/O块大小。
在一些例子中,MySQL甚至可以在不查阅数据文件的情况下从索引中读取行。如果索引中使用的所有列都是数字,则只使用索引树来解析查询。
在每一行被输出前,与having语句不匹配的行都被跳过。
一些非常快速的查询例子:
SELECT COUNT(*) FROM tbl_name;
SELECT MIN(key_part1),MAX(key_part1) FROM tbl_name;
SELECT MAX(key_part2) FROM tbl_name WHERE key_part1=constant;
SELECT ... FROM tbl_name ORDER BY key_part1,key_part2,... LIMIT 10;
SELECT ... FROM tbl_name ORDER BY key_part1 DESC, key_part2 DESC, ... LIMIT 10;
MySQL仅使用索引树来解决下面的查询,假设索引列是数值型的:
SELECT key_part1,key_part2 FROM tbl_name WHERE key_part1=val;
SELECT COUNT(*) FROM tbl_name WHERE key_part1=val1 AND key_part2=val2;
SELECT key_part2 FROM tbl_name GROUP BY key_part1;
下面的查询使用索引来检索排序后的行,而不需要单独的排序途径:
SELECT ... FROM tbl_name ORDER BY key_part1,key_part2,... ;
SELECT ... FROM tbl_name ORDER BY key_part1 DESC, key_part2 DESC, ... ;
PS:由于水平有限,译文中难免存在谬误,欢迎批评指正。