EXISTS与IN的子查询 - MySQL

时间:2013-01-07 06:08:55

标签: mysql sql phpmyadmin query-optimization subquery

以下两个查询是子查询。两者都是一样的,两者都适合我。但问题是方法1查询需要大约10秒才能执行,而方法2查询需要不到1秒。

我能够将方法1查询转换为方法2,但我不明白查询中发生了什么。我一直试图弄清楚自己。我真的想了解下面两个查询之间的区别是什么以及性能增益是如何发生的?它背后的逻辑是什么?

我是这些先进技术的新手。我希望有人能在这里帮助我。鉴于我阅读的docs并没有给我一些线索。

方法1:

SELECT
   *       
FROM
   tracker       
WHERE
   reservation_id IN (
      SELECT
         reservation_id                                 
      FROM
         tracker                                 
      GROUP  BY
         reservation_id                                 
      HAVING
         (
            method = 1                                          
            AND type = 0                                          
            AND Count(*) > 1 
         )                                         
         OR (
            method = 1                                              
            AND type = 1                                              
            AND Count(*) > 1 
         )                                         
         OR (
            method = 2                                              
            AND type = 2                                              
            AND Count(*) > 0 
         )                                         
         OR (
            method = 3                                              
            AND type = 0                                              
            AND Count(*) > 0 
         )                                         
         OR (
            method = 3                                              
            AND type = 1                                              
            AND Count(*) > 1 
         )                                         
         OR (
            method = 3                                              
            AND type = 3                                              
            AND Count(*) > 0 
         )
   )

方法2:

SELECT
   *                                
FROM
   `tracker` t                                
WHERE
   EXISTS (
      SELECT
         reservation_id                                              
      FROM
         `tracker` t3                                              
      WHERE
         t3.reservation_id = t.reservation_id                                              
      GROUP BY
         reservation_id                                              
      HAVING
         (
            METHOD = 1 
            AND TYPE = 0 
            AND COUNT(*) > 1
         ) 
         OR                                                     
         (
            METHOD = 1 
            AND TYPE = 1 
            AND COUNT(*) > 1
         ) 
         OR                                                    
         (
            METHOD = 2 
            AND TYPE = 2 
            AND COUNT(*) > 0
         ) 
         OR                                                     
         (
            METHOD = 3 
            AND TYPE = 0 
            AND COUNT(*) > 0
         ) 
         OR                                                     
         (
            METHOD = 3 
            AND TYPE = 1 
            AND COUNT(*) > 1
         ) 
         OR                                                     
         (
            METHOD = 3 
            AND TYPE = 3 
            AND COUNT(*) > 0
         )                                             
   )

4 个答案:

答案 0 :(得分:47)

Explain Plan会告诉您为什么要使用Exists。通常问题来自Exists vs Count(*)Exists更快。为什么呢?

  • 关于NULL提出的挑战:当子查询返回Null时,对于IN,整个查询变为Null 。所以你也需要处理它。但是使用Exist,它只是false。更容易应对。简单地IN无法与Null进行比较,但Exists可以。

  • e.g。 Exists (Select * from yourtable where bla = 'blabla');找到/匹配一个匹配时,你会得到真/假。

  • 在这种情况下,IN会根据Count(*)选择WHERE选择所有匹配行的位置,因为它会比较所有值。

但不要忘记这一点:

  • EXISTS高速执行IN:子查询结果非常大。
  • IN超前于EXISTS:子查询结果非常小。

参考了解更多详情:

答案 1 :(得分:3)

方法2很快,因为它使用EXISTS运算符,其中MySQL不会加载任何结果。 正如您在docs链接中所提到的那样,它忽略了SELECT子句中的任何内容。它仅检查匹配条件的第一个值,一旦找到它设置条件TRUE并移动以进行进一步处理。

另一方面,方法1有IN运算符,它加载所有可能的值然后匹配它。仅当找到完全匹配时,条件才会设置TRUE,这是一个耗时的过程。

因此你的方法2很快。

希望它有所帮助...

答案 2 :(得分:1)

EXISTS 运算符是一个布尔运算符,返回true或false。 subquery中经常使用EXISTS运算符来测试“ 存在”条件。

SELECT 
    select_list
FROM
    a_table
WHERE
    [NOT] EXISTS(subquery);

如果子查询返回任何行,则 EXISTS 运算符将返回true,否则将返回false。

此外, EXISTS 运算符一旦找到匹配的行,便立即终止进一步的处理。由于这种特性,在某些情况下,您可以使用 EXISTS 运算符来提高查询的性能。

运算符将对 EXISTS 运算符求反。换句话说,如果子查询不返回任何行,则 NOT EXISTS 返回true,否则返回false。

您可以在子查询中使用 SELECT * SELECT列 SELECT a_constant 。结果相同,因为MySQL忽略了 SELECT 子句中出现的 select_list

原因是 EXISTS 运算符基于“至少找到”原理工作。它返回true,并且一旦找到至少一个匹配的行,便停止扫描表。

另一方面,当 IN 运算符与子查询结合使用时,MySQL必须先处理子查询,然后使用子查询的结果来处理整个查询。

一般的经验法则是,如果子查询包含大量数据,则 EXISTS 运算符将提供更好的性能。

但是,如果子查询返回的结果集很小,则使用 IN 运算符的查询将执行得更快。

有关详细说明和示例:MySQL EXISTS - mysqltutorial.org

答案 3 :(得分:-2)

他们的官方文件。SubQuery Optimization with Exists