【问题标题】:Subqueries with EXISTS vs IN - MySQLEXISTS 与 IN 的子查询 - MySQL
【发布时间】:2012-12-20 21:03:47
【问题描述】:

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

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

我不熟悉这些先进技术。我希望有人会在这里帮助我。鉴于我阅读了docs 并没有给我任何线索。

方法一:

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 
         )
   )

方法二:

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
         )                                             
   )

【问题讨论】:

    标签: mysql sql phpmyadmin query-optimization subquery


    【解决方案1】:

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

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

    因此您的方法 2 很快。

    希望对你有帮助……

    【讨论】:

      【解决方案2】:

      第二种方法更快,因为你有这样的“WHERE t3.reservation_id = t.reservation_id”。在第一种情况下,您的子查询必须对表进行全面扫描以验证信息。然而,在 2o 方法中,子查询确切地知道它在寻找什么,一旦找到它就会检查有条件。

      【讨论】:

        【解决方案3】:

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

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

        • 例如Exists (Select * from yourtable where bla = 'blabla');找到/匹配一个命中的那一刻,你得到真/假。

        • 1234563

        但也不要忘记这一点:

        • EXISTSIN 高速执行:当子查询结果非常大时。
        • IN 领先于 EXISTS :当子查询结果非常小时。

        更多详情参考:

        【讨论】:

        • 20行是大还是小?
        • 到目前为止,我所有的测试都带有 IN 作为更好的优化查询。甚至解释选择也显示了对索引的更好使用。我还没有看到 Exists 比 In 更好的一个案例(大表也是如此。比如一个 1M 行和子表上 100k 行)。也许 Exists 在较新的 MySQL 版本中更加优化?
        【解决方案4】:

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

        【讨论】:

          【解决方案5】:

          EXISTS 运算符是返回真或假的布尔运算符。 EXISTS 运算符通常用于subquery 中以测试“exist”条件。

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

          如果子查询返回任意行,EXISTS操作符返回true,否则返回false。

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

          NOT 运算符否定 EXISTS 运算符。换句话说,如果子查询没有返回任何行,NOT EXISTS 返回 true,否则返回 false。

          您可以在子查询中使用 SELECT *SELECT columnSELECT a_constant 或任何内容。结果是一样的,因为 MySQL 忽略了出现在 SELECT 子句中的 select_list

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

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

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

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

          详细解释和示例:MySQL EXISTS - mysqltutorial.org

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2015-02-09
            • 1970-01-01
            • 1970-01-01
            • 2015-04-02
            • 2015-09-17
            • 1970-01-01
            • 2018-06-04
            • 2012-12-07
            相关资源
            最近更新 更多