我有这样的查询:
SELECT `table_1`.* from `table_1`
INNER JOIN `table_2` [...]
INNER JOIN `table_3` [...]
WHERE `table_1`.`id` IN(
SELECT `id` FROM [...]
)
AND [more conditions]
当我使用EXPLAIN时,最后会有'DEPENDENT SUBQUERY',但我希望在其他条件之前先执行此子查询。
有可能吗?
答案 0 :(得分:14)
SELECT `table_1`.*
FROM `table_1`
INNER JOIN
`table_2` [...]
INNER JOIN
`table_3` [...]
WHERE `table_1`.`id` IN
(
SELECT `id`
FROM [...]
)
AND [more conditions]
如果内部表被正确编入索引,则严格意义上的子查询根本不会“执行”。
由于子查询是IN
表达式的一部分,因此将条件推送到子查询中并将其转换为EXISTS
。
实际上,每个步骤都会对此子查询进行评估:
EXISTS
(
SELECT NULL
FROM [...]
WHERE id = table1.id
)
您实际上可以在EXPLAIN EXTENDED
提供的详细说明中看到它。
这就是为什么它被称为DEPENDENT SUBQUERY
:每次评估的结果取决于table1.id
的值。这样的子查询不相关,它是相关的优化版本。
MySQL
总是在更简单的过滤器之后评估EXISTS
子句(因为它们更容易评估,并且有可能根本不评估子查询)。
如果希望一次性评估子查询,请按以下步骤重写查询:
SELECT table_1.*
FROM (
SELECT DISTINCT id
FROM [...]
) q
JOIN table_1
ON table_1.id = q.id
JOIN table_2
ON [...]
JOIN table_3
ON [...]
WHERE [more conditions]
这会强制子查询在联接中处于领先地位,如果子查询与table_1
相比较小,则效率更高,如果子查询与table_1
相比较大,则效率较低。
如果子查询中使用的[...].id
上有索引,则子查询将使用INDEX FOR GROUP-BY
执行。
答案 1 :(得分:3)
这是mysql中的已知错误:http://bugs.mysql.com/bug.php?id=25926
一个有用的解决方法是将子查询下推到另一个select * from (subquery) as dt
类型子查询中。
答案 2 :(得分:2)
您可以先将子查询的结果选择到临时表中,然后在主查询中加入它。
答案 3 :(得分:2)
最好的方法是将table1的WHERE条件放入FROM子句中的子查询中。 EG:
SELECT `table_1`.*
FROM (
SELECT * FROM `table_1` WHERE `table_1`.`id` IN (...)
)
INNER JOIN `table_2` [...]
INNER JOIN `table_3` [...]
WHERE [more conditions]
答案 4 :(得分:-1)
不幸的是,不,你不能。子查询实际上为外部查询中的每一行运行一次。
我实际建议您将此转换为另一个联接,使用table_1.id
作为另一个表的键。