我目前正在使用MySQL 5.6.10。
我的实际查询更复杂,但这是一种重现问题的简单方法。我知道下面的查询是无用的(从x中选择id,其中id为(从...中选择id)),但它证明了我的观点。
我创建了这个表:
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);
得到了这个:
这对我来说非常有意义。但是如果我用另一个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行:
我知道查询可以重新编写为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行。
答案 0 :(得分:0)
一般来说,RDBMS无法优化子查询,也无法优化正确的表连接。正如Rewriting Subqueries as Joins(强调补充)中所述:
有时,除了使用子查询之外,还有其他方法可以测试一组值中的成员资格。此外,在某些情况下,不仅可以在没有子查询的情况下重写查询,但 使用其中一些技术而不是使用子查询可能更有效 。其中之一是
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;
执行计划是:
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+-------------------------------------------+ | ID | SELECT_TYPE | TABLE | TYPE | POSSIBLE_KEYS | KEY | KEY_LEN | REF | ROWS | EXTRA | +----+-------------+-------+--------+---------------+---------+---------+------------------+------+-------------------------------------------+ | 1 | SIMPLE | t1 | range | PRIMARY | PRIMARY | 4 | (null) | 9 | Using where; Using index; Using temporary | | 1 | SIMPLE | t2 | eq_ref | PRIMARY | PRIMARY | 4 | db_2_129b4.t1.id | 1 | Using index; Distinct | | 1 | SIMPLE | t3 | eq_ref | PRIMARY | PRIMARY | 4 | db_2_129b4.t1.id | 1 | Using index; Distinct | +----+-------------+-------+--------+---------------+---------+---------+------------------+------+-------------------------------------------+
在sqlfiddle上查看。