逻辑转换 - 非官方 MySQL 8.0 优化指南 - 学习笔记

MySQL 优化器可能会改变你的查询语句,但不影响查询结果。做这种转换的目的是重写语句,使其能减少工作量,让查询执行得更快。

考虑如下查询:

SELECT * FROM Country
WHERE population > 5000000 AND continent='Asia' AND 1=1;

这个条件语句是 AND,而 1=1总是为 true,所以是完全多余的。在执行查询时检查 1等于1 没有任何益处,去除这个条件仍然能返回相同结果。我们可以在 OPTIMIZER_TRACE 中看到 MySQL 应用了这种转换,同样还有其他若干转换:

...
  "join_optimization": {
    "select#": 1,
    "steps": [
      {
        "condition_processing": {
          "condition": "WHERE",
          "original_condition": "((`Country`.`Population` > 5000000) and (1 = 1))",
          "steps": [
            {
              "transformation": "equality_propagation",
              "resulting_condition": "((`Country`.`Population` > 5000000) and (1 = 1))"
            },
            {
              "transformation": "constant_propagation",
              "resulting_condition": "((`Country`.`Population` > 5000000) and (1 = 1))"
            },
            {
              "transformation": "trivial_condition_removal",        # 移除多余条件
              "resulting_condition": "(`Country`.`Population` > 5000000)"
            }
          ]
        }
      },
...

这种转换后的重写通过 EXPLAIN 后的 SHOW WARNINGS 也可以看到。查询重写后的语句如下(为便于阅读我添加了换行符、省略了相似内容):

EXPLAIN FORMAT=JSON SELECT * FROM Country WHERE population > 5000000 AND 1=1;
SHOW WARNINGS;
/* select#1 */
select `world`.`Country`.`Code` AS `Code`,
`world`.`Country`.`Name` AS `Name`,
`world`.`Country`.`Continent` AS `Continent`,...
from `world`.`Country` where (`world`.`Country`.`Population` > 5000000)

语句转换的例子

下面我们展示几个查询被逻辑转换的例子。注意几个例子中,主键和唯一键在执行查询前被转换了。

1. code 是主键;所有的查询值都被转换成了常量。

SELECT * FROM Country WHERE code='CAN'

转换后:

/* select#1 */
select 'CAN' AS `Code`,
'Canada' AS `Name`,
'North America' AS `Continent`,
'North America' AS `Region`,
'9970610.00' AS `SurfaceArea`,
'1867' AS `IndepYear`,...
from `world`.`Country` where 1

2. code 是主键;但查询的值不存于表中,where条件 不成立。

SELECT * FROM Country WHERE code='XYZ'

转换后:

/* select#1 */
select NULL AS `Code`,
NULL AS `Name`,
NULL AS `Continent`,
NULL AS `Region`,...
from `world`.`Country` where multiple equal('XYZ', NULL)

3. 同样是 where 不成立;查询的值存在,但条件 1=0 不成立。

SELECT * FROM Country WHERE code='CAN' AND 1=0

转换后:

/* select#1 */
select `world`.`Country`.`Code` AS `Code`,
`world`.`Country`.`Name` AS `Name`,
`world`.`Country`.`Continent` AS `Continent`,
`world`.`Country`.`Region` AS `Region`,
`world`.`Country`.`SurfaceArea` AS `SurfaceArea`,
`world`.`Country`.`IndepYear` AS `IndepYear`,...
from `world`.`Country` where 0

4. 派生表子查询;转换为 join 连表查询。

SELECT City.* FROM City, 
(SELECT * FROM Country WHERE continent='Asia') as Country 
WHERE Country.code=City.CountryCode AND Country.population > 5000000;

转换后:

/* select#1 */
select `world`.`City`.`ID` AS `ID`,
`world`.`City`.`Name` AS `Name`,
`world`.`City`.`CountryCode` AS `CountryCode`,
`world`.`City`.`District` AS `District`, 
`world`.`City`.`Population` AS `Population` 
from `world`.`City` join `world`.`Country` 
where (
(`world`.`Country`.`Continent` = 'Asia') and 
(`world`.`City`.`CountryCode` = ` world`.`Country`.`Code`) and 
(`world`.`Country`.`Population` > 5000000))

译自:
Logical Transformations – The Unofficial MySQL 8.0 Optimizer Guide

    原文作者:mokou591
    原文地址: https://www.jianshu.com/p/08d9ad65b5c1
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞