【问题标题】:Mysql Exists vs IN -- correlated subquery vs subquery?Mysql Exists vs IN - 相关子查询与子查询?
【发布时间】:2014-09-10 01:46:11
【问题描述】:

我很好奇EXISTS() 的执行速度应该比IN() 快。

当 Bill Karwin 提出一个好观点时,我是 answering a question。当您使用EXISTS() 时,它使用的是相关子查询(相关子查询),而 IN() 仅使用子查询。

EXPLAIN 显示 EXISTSNOT EXISTS 都使用依赖子查询,而 IN / NOT IN 都只使用子查询。所以我很好奇相关子查询比子查询更快??

我以前使用过 EXISTS,它的执行速度确实比 IN 快,这就是我感到困惑的原因。

这里是SQLFIDDLE 的解释

EXPLAIN SELECT COUNT(t1.table1_id) 
FROM table1 t1 
WHERE EXISTS
(   SELECT 1 
    FROM table2 t2
    WHERE t2.table1_id <=> t1.table1_id
);

+-------+-----------------------+-----------+-------+---------------+-----------+--------+--------------------------+--------+------------------------------+
| ID    |   SELECT_TYPE         |   TABLE   | TYPE  | POSSIBLE_KEYS |   KEY     |KEY_LEN |  REF                     |   ROWS |  EXTRA                       |
+-------+-----------------------+-----------+-------+---------------+-----------+--------+--------------------------+--------+------------------------------+
|  1    |   PRIMARY             |   t1      | index | (null)        |   PRIMARY |   4    | (null)                   |   4    |  Using where; Using index    |
|  2    |   DEPENDENT SUBQUERY  |   t2      | REF   | table1_id     |  table1_id|   4    | db_9_15987.t1.table1_id  |   1    |  Using where; Using index    |
+-------+-----------------------+-----------+-------+---------------+-----------+--------+--------------------------+--------+------------------------------+

EXPLAIN SELECT COUNT(t1.table1_id) 
FROM table1 t1 
WHERE NOT EXISTS
(   SELECT 1 
    FROM table2 t2
    WHERE t2.table1_id = t1.table1_id
);
+-------+-----------------------+-----------+-------+---------------+-----------+--------+--------------------------+--------+------------------------------+
| ID    |   SELECT_TYPE         |   TABLE   | TYPE  | POSSIBLE_KEYS |   KEY     |KEY_LEN |  REF                     |   ROWS |  EXTRA                       |
+-------+-----------------------+-----------+-------+---------------+-----------+--------+--------------------------+--------+------------------------------+
|  1    |   PRIMARY             |   t1      | index | (null)        |   PRIMARY |   4    | (null)                   |   4    |  Using where; Using index    |
|  2    |   DEPENDENT SUBQUERY  |   t2      | ref   | table1_id     |  table1_id|   4    | db_9_15987.t1.table1_id  |   1    |  Using index                 |
+-------+-----------------------+-----------+-------+---------------+-----------+--------+--------------------------+--------+------------------------------+

EXPLAIN SELECT COUNT(t1.table1_id) 
FROM table1 t1 
WHERE t1.table1_id NOT IN 
(   SELECT t2.table1_id 
    FROM table2 t2
);
+-------+-------------------+-----------+-------+---------------+-----------+--------+----------+--------+------------------------------+
| ID    |   SELECT_TYPE     |   TABLE   | TYPE  | POSSIBLE_KEYS |   KEY     |KEY_LEN |  REF     |   ROWS |  EXTRA                       |
+-------+-------------------+-----------+-------+---------------+-----------+--------+----------+--------+------------------------------+
|  1    |   PRIMARY         |   t1      | index | (null)        |   PRIMARY |   4    | (null)   |   4    |  Using where; Using index    |
|  2    |   SUBQUERY        |   t2      | index | (null)        |  table1_id|   4    | (null)   |   2    |  Using index                 |
+-------+-------------------+-----------+-------+---------------+-----------+--------+----------+--------+------------------------------+

几个问题

在上面的解释中,EXISTS 如何在 extras 中有 using whereusing index 而 NOT EXISTS 在 extras 中没有 using where

如何关联子查询比子查询快?

【问题讨论】:

  • 那么你有没有exists 执行速度更快的repro?另外你在哪个版本上体验过这个? in 还有used to have the same problem
  • @MartinSmith 好吧,大约一年前,我将查询从 IN 切换到 EXISTS,因为 EXISTS 执行得更快(而不是快了半秒到一秒).. 但我刚买了一台新电脑并下载了最新版本的 MySQL。我刚刚运行了一个查询,IN 运行速度快了 0.004 秒...最近执行计划/优化器有修复吗?
  • 我对 MySql 优化器了解不多,但我相信 5.6 引入了一些变化。 dev.mysql.com/doc/refman/5.6/en/subquery-optimization.html
  • @MartinSmith 在阅读完该文档后,似乎如果没有 group by 或任何聚合 IN 将执行得更快......如果存在将执行得更快......我已经用在我的本地测试。你应该考虑用这个和你为 bill karwin 发布的其他链接写一个答案

标签: mysql


【解决方案1】:

这是一个与 RDBMS 无关的答案,但可能会有所帮助。在我的理解中,相关(又名,依赖)子查询可能是最常被错误指责的导致性能不佳的罪魁祸首。

问题(正如它最常描述的那样)是它为外部查询的每一行处理内部查询。因此,如果外部查询返回 1,000 行,而内部查询返回 10,000 行,那么您的查询必须经过 10,000,000 行(外部×内部)才能产生结果。与来自相同结果集的非相关查询的 11,000 行(外部 + 内部)相比,这并不好。

但是,这只是最坏的情况。在许多情况下,DBMS 将能够利用索引来大幅减少行数。即使只有内部查询可以使用索引,这 10,000 行也会变成约 13 次查找,这会将总数降至 13,000。

exists 运算符可以在第一行之后停止处理行,从而进一步降低查询成本,尤其是当大多数外部行至少匹配一个内部行时。

在极少数情况下,我看到 SQL Server 2008R2 将相关子查询优化为合并连接(仅遍历两个集合 - 最好的情况),在这种情况下,可以在内部和外部查询中找到合适的索引。

性能不佳的真正罪魁祸首不一定是相关子查询,而是嵌套扫描

【讨论】:

  • 我认为是这样。虽然 RDBMS 引擎可以在内部进行任何类型的转换,所以我们永远不知道,但如果我们完全遵循问题,则需要在关联子查询时完全执行子查询 - 仅用于匹配行。如果IN 子查询会产生很大的结果集,而我们只对有限数量的行感兴趣,则相关子查询可能会跳过一些工作。不过,MrTux 的回答也有一定道理。
【解决方案2】:

这取决于 MySQL 版本 - MySQL 查询优化器在最高 6.0 版本中存在错误。

带有“IN”的子查询未正确优化(但像依赖查询一样一次又一次地执行)。此错误不会影响exists 查询或连接。

问题在于,对于使用 IN 子查询的语句, 优化器将其重写为相关子查询。考虑以下 使用不相关子查询的语句:

SELECT ... FROM t1 WHERE t1.a IN (SELECT b FROM t2);

优化器将语句重写为相关子查询:

SELECT ... FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.b = t1.a);

如果内部和外部查询分别返回 M 和 N 行,则 执行时间变成了 O(M×N) 的数量级,而不是 O(M+N) 这将是一个不相关的子查询。

参考。

【讨论】:

  • `O(MxN) 的情况看起来好像原则上不应该适用于 OP,因为相关子查询可以使用索引。在 MySQL 中,当它评估内部不相关的子查询时,它可能仍然必须将它存储在某个地方并最终以与嵌套循环连接的内侧几乎相同的方式重播物化结果?在什么情况下物化结果被索引?还是会在这里使用哈希连接或其他东西?
【解决方案3】:

如您所知,子查询不使用外部查询的值,因此它只执行一次。相关子查询是同步的,因此它会针对外部查询中处理的每一行执行。

使用EXISTS 的好处是,如果认为满足,子查询的执行在返回至少一行后停止。因此,它可以比简单的子查询更快。但这不是一般规则!所有这些都取决于您正在执行的查询、查询优化器和 SQL 执行引擎版本。

EXISTS 建议在你有 if 条件语句时使用,因为它肯定比 count 快​​。

您无法使用 4 或 3 个查询的简单基准来真正比较两个子查询。

希望有用!

【讨论】:

    猜你喜欢
    • 2012-12-20
    • 1970-01-01
    • 1970-01-01
    • 2011-02-10
    • 1970-01-01
    • 2021-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多