Mysql Exists vs IN - 相关子查询与子查询?

时间:2014-09-10 01:46:12

标签: mysql

我很好奇EXISTS()的执行应该如何比IN()更快。

当比尔·卡尔温提出一个好点时,我才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                 |
+-------+-------------------+-----------+-------+---------------+-----------+--------+----------+--------+------------------------------+

FEW问题

在上面的解释中,EXISTS如何在额外内容中using whereusing index但是EXESTS在附加内容中没有using where

相关子查询如何比子查询更快?

3 个答案:

答案 0 :(得分:9)

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

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

然而,这只是最糟糕的情况。在许多情况下,DBMS将能够利用索引来大幅减少行数。即使只有内部查询可以使用索引,10,000行也会变成~13次搜索,这会使总数下降到13,000。

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

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

性能不佳的真正原因不一定是相关子查询,而是嵌套扫描

答案 1 :(得分:3)

这取决于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)   这将是一个不相关的子查询。

参考文献

答案 2 :(得分:1)

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

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

建议在EXISTS条件语句时使用

if,因为它肯定比count更快。

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

希望它有用!