子查询与连接

Posted

技术标签:

【中文标题】子查询与连接【英文标题】:Subqueries vs joins 【发布时间】:2010-09-13 13:35:26 【问题描述】:

我重构了我们从另一家公司继承的应用程序的慢速部分,以使用内部联接而不是子查询,例如:

WHERE id IN (SELECT id FROM ...)

重构后的查询运行速度提高了大约 100 倍。(约 50 秒到约 0.3 秒)我预计会有改进,但谁能解释为什么它如此激烈? where 子句中使用的列都已编入索引。 SQL 是否每行执行一次 where 子句中的查询?

更新 - 解释结果:

区别在于“where id in ()”查询的第二部分——

2   DEPENDENT SUBQUERY  submission_tags ref st_tag_id   st_tag_id   4   const   2966    Using where

vs 1 个带有连接的索引行:

    SIMPLE  s   eq_ref  PRIMARY PRIMARY 4   newsladder_production.st.submission_id  1   Using index

【问题讨论】:

Join vs. sub-query的可能重复 不是重复的。这个问题专门针对显着的性能差异。另一个问题更笼统,更开放,关于每种方法的优缺点以及为什么一种方法似乎更受欢迎。 @simhumileco 这没有任何改进,没有区别,这与作者所写的内容相反,并且对代码样式进行的那种编辑是不合适的。 When should I make edits to code? 嗨@philipxy,我无意干涉作者的想法,只是为了让代码片段更易读,写得更仔细。 【参考方案1】:

在每个版本上运行解释计划,它会告诉你原因。

【讨论】:

【参考方案2】:

查看每个查询的查询计划。

Where inJoin可以通常使用相同的执行计划来实现,所以通常为零加速它们之间的变化。

【讨论】:

哈哈,我 【参考方案3】:

您正在为每一行运行一次子查询,而连接发生在索引上。

【讨论】:

我认为这不是真的。 SQL 引擎应该只运行一次子查询并将结果用作列表。 这取决于 - 如果子查询以某种方式与外部查询相关(使用它的数据),它会在每一行中执行。 在这种情况下可能是这样,但一般情况下并非如此。 OP 的 EXPLAINDEPENDENT SUBQUERY,这是这种行为最明显的指标。【参考方案4】:

优化器做得不是很好。通常它们可以毫无区别地进行转换,优化器可以做到这一点。

【讨论】:

【参考方案5】:

对于子查询,您必须为每个结果重新执行第二次 SELECT,并且每次执行通常返回 1 行。

使用连接时,第二个 SELECT 会返回更多行,但您只需执行一次。优点是现在你可以加入结果,加入关系是数据库应该擅长的。例如,也许优化器现在可以发现如何更好地利用索引。

【讨论】:

【参考方案6】:

与其说是子查询,不如说是 IN 子句,尽管连接至少是 Oracle 的 SQL 引擎的基础并且运行速度极快。

【讨论】:

真正的本质并不坏。【参考方案7】:

子查询可能正在执行“全表扫描”。换句话说,不使用索引并返回主查询中的 Where 需要过滤掉的太多行。

当然只是猜测,没有细节,但这是常见的情况。

【讨论】:

【参考方案8】:

通常它的结果是优化器无法确定子查询可以作为连接执行,在这种情况下,它会为表中的每条记录执行子查询,而不是将子查询中的表与您的表连接起来正在查询。一些更“企业”的数据库在这方面做得更好,但他们有时还是会错过。

【讨论】:

【参考方案9】:

“相关子查询”(即,其中 where 条件取决于从包含查询的行获得的值)将为每一行执行一次。一个不相关的子查询(其中 where 条件独立于包含的查询)将在开始时执行一次。 SQL 引擎会自动进行这种区分。

但是,是的,解释计划会给你一些肮脏的细节。

【讨论】:

请注意,DEPENDENT SUBQUERY 与“相关子查询”的含义完全相同。【参考方案10】:

这个问题有点笼统,所以这里给出一个笼统的答案:

基本上,当 mysql 有大量行需要排序时,查询会花费更长的时间。

这样做:

对每个查询(加入的查询,然后是子查询的查询)运行 EXPLAIN,并在此处发布结果。

我认为看到 MySQL 对这些查询的解释的不同对每个人来说都是一次学习经历。

【讨论】:

【参考方案11】:

在针对数据集运行查询之前,它们会通过查询优化器,优化器会尝试以能够尽快从结果集中删除尽可能多的元组(行)的方式来组织查询。通常,当您使用子查询(尤其是坏查询)时,在外部查询开始运行之前,无法从结果集中删除元组。

如果没有看到查询,很难说出原始查询有什么不好的地方,但我的猜测是优化器无法做得更好。运行“解释”将向您展示用于检索数据的优化器方法。

【讨论】:

【参考方案12】:

where 子查询必须为每个返回的行运行 1 个查询。内连接只需要运行 1 个查询。

【讨论】:

【参考方案13】:

这是subqueries are evaluated in MySQL 6.0的示例。

新的优化器会将这种子查询转换为连接。

【讨论】:

这是一篇关于 MySQL 6.0 改进优化器的精彩文章,谢谢【参考方案14】:

摘自参考手册 (14.2.10.11 Rewriting Subqueries as Joins):

LEFT [OUTER] JOIN 可以比等效的子查询更快,因为服务器可能能够更好地优化它——这一事实并非仅针对 MySQL 服务器。

所以子查询可能比 LEFT [OUTER] JOINS 慢。

【讨论】:

以上是关于子查询与连接的主要内容,如果未能解决你的问题,请参考以下文章

子查询与连接

MySQL-子查询与连接

使用子查询更新与使用连接更新 - 性能更好

如何将查询与子查询连接起来?

子查询与连接

连接查询与子查询