【问题标题】:Does the MySQL IN clause execute the subquery multiple times?MySQL IN 子句是否多次执行子查询?
【发布时间】:2013-09-18 09:44:27
【问题描述】:

鉴于 MySQL 中的这个 SQL 查询:

SELECT * FROM tableA WHERE tableA.id IN (SELECT id FROM tableB);

MySQL 是否对tableA 中的每一行多次执行子查询SELECT id FROM tableB

有没有办法在不使用变量或存储过程的情况下让 sql 运行得更快?

为什么这通常比使用LEFT JOIN 慢?

【问题讨论】:

    标签: mysql database-performance


    【解决方案1】:

    你的假设是错误的;子查询将只执行一次。它比连接慢的原因是IN 不能利用索引;每次评估 WHERE 子句时,它必须扫描其参数一次,即 tableA 中的每行一次。您可以优化查询,而无需使用变量或存储过程,只需将 IN 替换为连接即可,因此:

    SELECT tableA.field1, tableA.field2, [...]
    FROM tableA 
      INNER JOIN tableB ON tableA.id = tableB.id
    

    除非您不介意从两个表中取回每个字段,否则您确实需要在SELECT 子句中枚举您想要的字段;例如,tableA.* 会引发语法错误。

    【讨论】:

    • +1。 LEFT JOIN 也不是一回事。 INNER JOIN 就是答案。
    • @Cfreak 好吧,如果我想挑剔一点,我可以争辩说 LEFT JOIN tableB on tableA.id = tableB.id AND tableB.id IS NOT NULL 如果更冗长的话也会有同样的效果,但那太愚蠢了。
    • 你确定在 MySQL 中子查询只会执行一次吗?保证从何而来?
    • 没有索引!这解释了很多,并且为每一行枚举IN 的值也会减慢它的速度。感谢您的澄清。
    • @newtover 来自经验;随意使用任何配置为使用一般查询日志的 MySQL 5.0 实例自行确认。
    【解决方案2】:

    首先,这取决于 MySQL 的版本。我相信 5.6 版正确优化了此类查询。 MySQL 文档对此不一致。例如,here 它说明了一件事:

    考虑以下子查询比较:

    outer_expr IN (SELECT inner_expr FROM ... WHERE subquery_where)
    

    MySQL “从外到内”评估查询。也就是说,它首先 获取外部表达式outer_expr的值,然后运行 子查询并捕获它产生的行。

    这种“从外到内”意味着对每一行评估子查询。这与我使用 MySQL 的经验一致。

    文档另有建议here:

    MySQL 本身进行的一些优化是:

    • MySQL 只执行一次不相关的子查询。使用 EXPLAIN 确保给定的子查询确实是不相关的。
    • MySQL 重写 IN、ALL、ANY 和 SOME 子查询以尝试利用子查询中的选择列表列被索引的可能性。

    我相信该声明指的是in 子句。可能发生的情况是子查询被重写为相关子查询以检查索引,然后运行多次(无论是否存在索引)。

    【讨论】:

    • 当然,有人对此不确定,请看一下这个问题:“MySQL“IN”查询在子查询中非常慢,但在显式值下很快”-stackoverflow.com/questions/5018284/…
    • @TheAnhNguyen 。 . .这个问题是 10 岁的。此答案中引用的 MySQL 版本是 8 年前。
    • 昨天,我对 MariaDB 5.5.60(2018 年发布)有同样的问题。所以我也想知道什么 RBDMS 正确优化了这样的查询,反之亦然。
    • 哦,通过 MySQL 8.0 参考链接 (dev.mysql.com/doc/refman/8.0/en/subquery-materialization.html)。我稍后会用它做一个测试。它说:优化器使用物化来实现更有效的子查询处理。物化通过将子查询结果生成为临时表(通常在内存中)来加速查询执行。 MySQL 第一次需要子查询结果时,会将结果具体化到一个临时表中。任何后续需要结果时,MySQL 都会再次引用临时表。
    猜你喜欢
    • 1970-01-01
    • 2015-04-02
    • 2020-10-13
    • 2014-07-30
    • 2014-07-07
    • 1970-01-01
    • 2016-08-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多