对于一些执行起来十分耗费性能的语句,MySQL 还是依据一些规则,竭尽全
力的把这个很糟糕的语句转换成某种可以比较高效执行的形式,这个过程也可以
被称作查询重写

一、条件化简

  1. 移除不必要的括号

OR ((a > c) AND (c < 5))) —->OR (a > c AND c < 5)

  1. 常量传递

a = 5 AND b > a 就可以被转换为: a = 5 AND b > 5

  1. 移除没用的条件

对于一些明显永远为 TRUE 或者 FALSE 的表达式,优化器会移除掉它们,比如 1=1

  1. 表达式计算

    a = 5 + 1 被化简为a = 6 但ABS(a) > 5 ,-a < -8此类不会化简(不要对索引列做骚操作)

  2. 常量表检测

MySQL 觉得下边这种查询运行的特别快:
使用主键等值匹配或者唯一二级索引列等值匹配作为搜索条件来查询某个
表。
MySQL 觉得这两种查询花费的时间特别少,少到可以忽略,所以也把通过这
两种方式查询的表称之为常量表(英文名:constant tables)。优化器在分析一
个查询语句时,先首先执行常量表查询,然后把查询中涉及到该表的条件全部替
换成常数,最后再分析其余表的查询成本,比方说这个查询语句:
SELECT FROM table1 INNER JOIN table2
ON table1.column1 = table2.column2
WHERE table1.primary_key = 1;
很明显,这个查询可以使用主键和常量值的等值匹配来查询 table1 表,也就
是在这个查询中 table1 表相当于常量表,在分析对 table2 表的查询成本之前,
就会执行对 table1 表的查询,并把查询中涉及 table1 表的条件都替换掉,也就
是上边的语句会被转换成这样:SELECT table1 表记录的各个字段的常量值, table2.
FROM table1 INNER JOIN
table2 ON table1 表 column1 列的常量值 = table2.column2;

  1. 外连接消除

外连接和内连接的区别在于驱动表的没有匹配上的用null填充,所以当过滤条件中指定被驱动表的相关列不为null则可以转化为内连接
(空值拒绝)
好处是优化器可以自主选择成本低的表作为驱动表

二、子查询优化

子查询分类

按返回的结果集区分子查询:

标量子查询:单一值
只返回一个单一值的子查询
SELECT (SELECT m1 FROM e1 LIMIT 1);
SELECT FROM e1 WHERE m1 = (SELECT MIN(m2) FROM e2);
行子查询:单行单列
SELECT
FROM e1 WHERE (m1, n1) = (SELECT m2, n2 FROM e2 LIMIT 1);
列子查询: 单列
SELECT FROM e1 WHERE m1 IN (SELECT m2 FROM e2);
表子查询: 多行多列
SELECT
FROM e1 WHERE (m1, n1) IN (SELECT m2, n2 FROM e2);

按与外层查询关系来区分子查询:

不相关子查询:子查询可以单独运行出结果,而不依赖于外层查询的值
相关子查询:子查询的执行需要依赖于外层查询的值
SELECT * FROM e1 WHERE m1 IN (SELECT m2 FROM e2 WHERE n1 = n2);

IN/ANY/SOME/ALL 子查询:

in /not in:某个操作数存在/不存在于子查询的集合中
any/some: 等价于 min(子查询)
SELECT FROM e1 WHERE m1 > ANY(SELECT m2 FROM e2);等价于
SELECT
FROM e1 WHERE m1 > (SELECT MIN(m2) FROM e2);
all:等价于 max(子查询)

子查询中不允许有 LIMIT 语句,
而且这类子查询中 ORDER BY 子句、DISTINCT 语句、没有聚集函数以及 HAVING
子句的 GROUP BY 子句没有什么意义。因为子查询的结果其实就相当于一个集合,
集合里的值排不排序等一点儿都不重要。

EXISTS 子查询:

有的时候我们仅仅需要判断子查询的结果集中是否有记录,而不在乎它的记
录具体是个啥,可以使用把 EXISTS 或者 NOT EXISTS 放在子查询语句前边,就像
这样:
SELECT * FROM e1 WHERE EXISTS (SELECT 1 FROM e2);
只要(SELECT 1 FROM e2)这个查询中有记录,那么整个 EXISTS 表达式的结果就为 TRUE