Mysql在where子句中优化子查询

Posted

技术标签:

【中文标题】Mysql在where子句中优化子查询【英文标题】:Mysql optimize subquery in where clause 【发布时间】:2017-01-28 18:02:56 【问题描述】:

如果我执行查询,例如

SELECT * 
FROM `table1` 
WHERE 1 OR EXISTS (
                    SELECT show 
                    FROM `table2` 
                    WHERE id IN(1,2,3)
                  )

我希望优化器意识到 where 子句总是解析为 true,因此没有必要运行子查询。使用 EXPLAIN 运行查询表明情况并非如此,并且无论如何都会执行子查询。 这是一个更复杂问题的缩小示例,我尝试根据外部查询的列值执行不同的子查询,例如:

SELECT value FROM table t
LEFT JOIN...
WHERE
 (SELECT
   IF(t.value = 1,
       (SELECT ...),
       (SELECT ...)
     )
 )

意思是 where 条件中只有一个内部子查询被执行,但这里也发生了同样的情况,两者都被执行,但只使用了一个的值。所以结果是正确的,但运行了无用的查询。我已经尝试过 CASE WHEN 以及同样的问题。不确定是因为我使用 MariaDB 还是我在这里缺少的东西。

【问题讨论】:

请提供您正在使用的 MariaDB 的版本号。并请提供EXPLAIN SELECT ... 类似的查询在 5.6.22 中“短路”失败。 5.7.15、8.0.0 和 MariaDB 10.2.2 同上。 【参考方案1】:

对不起,我希望我能正确理解您的问题。在 WHERE 1 的第一个查询中,mysql 从不执行 EXISTS 子查询。 EPLAIN 只向您展示可以做什么。您可以通过 SHOW status LIKE 'Handler_read_first'; 看到它很简单。我有 2 个表。如果我使用 WHERE 1 进行测试,它的唯一增量为 1(读取 1 个表),没有它的增量为 2

样本

表格

MariaDB [test]> select * from index_usage;
+----+------+------+------+-------+
| id | a    | b    | c    | dummy |
+----+------+------+------+-------+
| 11 | 1    | 1    | 1    | a     |
+----+------+------+------+-------+
1 row in set (0.00 sec)

MariaDB [test]> select * from index_usage_copy;
+----+------+------+------+-------+
| id | a    | b    | c    | dummy |
+----+------+------+------+-------+
| 99 | 1    | 1    | 1    | a     |
+----+------+------+------+-------+
1 row in set (0.00 sec)

柜台

MariaDB [test]> SHOW status LIKE 'Handler_read_first';
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| Handler_read_first | 59    |
+--------------------+-------+
1 row in set (0.00 sec)

第一个查询(没有 WHERE 1)递增 2

    MariaDB [test]> SELECT * from index_usage WHERE EXISTS ( SELECT 1 FROM index_usage_copy where id in (1,2,99)); SHOW status LIKE 'Handler_read_first';
    +----+------+------+------+-------+
    | id | a    | b    | c    | dummy |
    +----+------+------+------+-------+
    | 11 | 1    | 1    | 1    | a     |
    +----+------+------+------+-------+
    1 row in set (0.00 sec)

    +--------------------+-------+
    | Variable_name      | Value |
    +--------------------+-------+
    | Handler_read_first | 61    |
    +--------------------+-------+
    1 row in set (0.00 sec)

MariaDB [test]> SELECT * from index_usage WHERE EXISTS ( SELECT 1 FROM index_usage_copy where id in (1,2,99)); SHOW status LIKE 'Handler_read_first';
+----+------+------+------+-------+
| id | a    | b    | c    | dummy |
+----+------+------+------+-------+
| 11 | 1    | 1    | 1    | a     |
+----+------+------+------+-------+
1 row in set (0.00 sec)

+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| Handler_read_first | 63    |
+--------------------+-------+
1 row in set (0.00 sec)

MariaDB [test]> SELECT * from index_usage WHERE EXISTS ( SELECT 1 FROM index_usage_copy where id in (1,2,99)); SHOW status LIKE 'Handler_read_first';
+----+------+------+------+-------+
| id | a    | b    | c    | dummy |
+----+------+------+------+-------+
| 11 | 1    | 1    | 1    | a     |
+----+------+------+------+-------+
1 row in set (0.00 sec)

+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| Handler_read_first | 65    |
+--------------------+-------+
1 row in set (0.00 sec)

现在使用 WHERE 1 - 仅递增 1

MariaDB [test]> SELECT * from index_usage WHERE 1 OR EXISTS ( SELECT 1 FROM index_usage_copy where id in (1,2,99)); SHOW status LIKE 'Handler_read_first';
+----+------+------+------+-------+
| id | a    | b    | c    | dummy |
+----+------+------+------+-------+
| 11 | 1    | 1    | 1    | a     |
+----+------+------+------+-------+
1 row in set (0.00 sec)

+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| Handler_read_first | 66    |
+--------------------+-------+
1 row in set (0.00 sec)

MariaDB [test]> SELECT * from index_usage WHERE 1 OR EXISTS ( SELECT 1 FROM index_usage_copy where id in (1,2,99)); SHOW status LIKE 'Handler_read_first';
+----+------+------+------+-------+
| id | a    | b    | c    | dummy |
+----+------+------+------+-------+
| 11 | 1    | 1    | 1    | a     |
+----+------+------+------+-------+
1 row in set (0.00 sec)

+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| Handler_read_first | 67    |
+--------------------+-------+
1 row in set (0.00 sec)

MariaDB [test]>

【讨论】:

【参考方案2】:

另外,EXPLAIN FORMAT=JSON 表示 5.6 和 5.7 的“optimized_away_subqueries”。 8.0 和 MariaDB 10.2 和 10.3 并没有这么说,但似乎无论如何都摆脱了子查询。

【讨论】:

以上是关于Mysql在where子句中优化子查询的主要内容,如果未能解决你的问题,请参考以下文章

Oracle 查询优化器是不是将*** where 子句应用于子查询或视图?

子选择查询是不是基于它之外的 WHERE 子句进行了优化? [关闭]

优化where子句中的SQL子查询

mysql子查询在where in子句中

WHERE 子句中的 SQL 查询子选择优化 (SQL Server)

7_mysql查询之where子句