3

我目前正在使用 MySQL 5.6.10。

我的实际查询更复杂,但这是重现问题的简单方法。我知道下面的查询是无用的(select id from x where id in (select id from x...)),但它证明了我的观点。

我创建了这张表:

CREATE  TABLE test (
  id INT NOT NULL AUTO_INCREMENT ,
  PRIMARY KEY (id));

然后运行此命令 5 次 - 它在表中创建了 50 行:

INSERT INTO test (id) VALUES(null),(null),(null),(null),(null),(null),(null),(null),(null),(null);

然后运行这个解释:

EXPLAIN SELECT id FROM test WHERE 
       id in (SELECT id FROM test WHERE id < 5);

得到了这个: 4行

这对我来说很有意义。但是,如果我用另一个 IN 在 WHERE 子句中添加一个 OR,如下所示:

EXPLAIN SELECT id FROM test WHERE 
       id IN (SELECT id FROM test WHERE id < 5)
    OR id IN (SELECT id FROM test WHERE id > 45);

突然 MySQL 正在查看所有 50 行: 50 行

我知道查询可以重写为SELECT id FROM test WHERE id < 5 OR id > 45或 UNION 等,这不是重点。关键是 MySQL 正在检查太多的行。

如果我在第一个查询中运行 FLUSH STATUS / SHOW STATUS LIKE "Handler%",这就是我得到的:

Handler_read_key 5
Handler_external_lock 4
Handler_read_next 4
Handler_read_first 1

但是,如果我对第二个查询这样做,我会得到:

Handler_read_key 99
Handler_write 9
Handler_external_lock 6
Handler_read_next 59
Handler_read_first 2

为什么差别很大?我想知道它是否是优化器,如果是,是否可以在查询中包含一些选项来阻止这种“优化”?这对我正在开发的查询具有真正的实际意义。MySQL 不是只检查几百行,而是检查 120,000 行。

4

1 回答 1

0

一般来说,RDBMS 不能优化子查询,也不能优化适当的表连接。如将子查询重写为联接(添加了重点)中所述:

有时,除了使用子查询之外,还有其他方法可以测试一组值中的成员资格。此外,在某些情况下,不仅可以在没有子查询的情况下重写查询,而且使用其中一些技术比使用子查询更有效。其中之一是IN()构造:

例如,这个查询:

SELECT * FROM t1 WHERE id IN (SELECT id FROM t2);

可以改写为:

SELECT DISTINCT t1.* FROM t1, t2 WHERE t1.id=t2.id;

在您的抽象情况下(即忽略现实中对该查询所做的其他明显改进):

SELECT DISTINCT t1.*
FROM   test t1
  JOIN test t2 USING (id)
  JOIN test t3 USING (id)
WHERE  t2.id < 5
    OR t3.id > 45;

其执行计划为:

+----+-------------+--------+--------+------------- --+---------+---------+------+------+- ----------------------------------------------------------+
| 身份证 | 选择类型 | 表 | 类型 | 可能键 | 关键 | KEY_LEN | 参考 | 行 | 额外 |
+----+-------------+--------+--------+------------- --+---------+---------+------+------+- ----------------------------------------------------------+
| 1 | 简单 | t1 | 范围 | 初级 | 初级 | 4 | (空) | 9 | 使用哪里;使用索引;使用临时 |
| 1 | 简单 | t2 | eq_ref | 初级 | 初级 | 4 | db_2_129b4.t1.id | 1 | 使用索引;区别 |
| 1 | 简单 | t3 | eq_ref | 初级 | 初级 | 4 | db_2_129b4.t1.id | 1 | 使用索引;区别 |
+----+-------------+--------+--------+------------- --+---------+---------+------+------+- ----------------------------------------------------------+

sqlfiddle上查看。

于 2013-05-25T00:56:01.347 回答