为啥多个 EXISTS 会破坏查询

Posted

技术标签:

【中文标题】为啥多个 EXISTS 会破坏查询【英文标题】:Why do multiple EXISTS break a query为什么多个 EXISTS 会破坏查询 【发布时间】:2018-12-04 16:30:48 【问题描述】:

我正在尝试包含一个新表,其中包含需要检查并包含在存储过程中的值。语句 1 是需要检查的现有表,而语句 2 是要检查的新表。

我目前有 2 个EXISTS 条件独立运行并产生我期望的结果。我的意思是如果我注释掉语句 1,语句 2 有效,反之亦然。当我将它们放在一起时,查询没有完成,没有错误但它超时,这是意外的,因为每个语句只需要几秒钟。

我知道可能有更好的方法来做到这一点,但在我这样做之前,我想知道为什么我似乎无法执行多个这样的存在语句? WHERE 子句中是否不存在多个 EXISTS 条件?

SELECT *
FROM table1 S    
WHERE
--Statement 1
EXISTS 
(
   SELECT 1
   FROM table2 P WITH (NOLOCK)
      INNER JOIN table3 SA ON SA.ID = P.ID
   WHERE P.DATE = @Date AND P.OTHER_ID = S.ID
   AND 
   (
      SA.FILTER = ''
      OR 
      (
          SA.FILTER = 'bar'
          AND 
          LOWER(S.OTHER) = 'foo'
       )
    )
)
OR 
(
   --Statement 2
   EXISTS 
   (
      SELECT 1
      FROM table4 P WITH (NOLOCK)
         INNER JOIN table5 SA ON SA.ID = P.ID
      WHERE P.DATE = @Date 
            AND P.OTHER_ID = S.ID 
            AND LOWER(S.OTHER) = 'foo'
    )
)

编辑:我已包含查询详细信息。表 1-5 代表不同的表,没有重复的表。

【问题讨论】:

不是我的反对票,而是我can't reproduce this 如果您的查询超时,我会检查执行计划,看看是什么让您的查询膨胀。您只发布了伪代码,这使得这非常困难。 @scsimon 我很惊讶它是查询本身。我已经包含了详细信息。 【参考方案1】:

评论太长了。

您所写的查询似乎是正确的。超时只能从执行计划中排除,但这里有一些可能发生的事情或您可以从中受益。

@Date 上的参数嗅探。尝试对这个值进行硬编码,看看你是否仍然遇到同样的缓慢 P.OTHER_IDP.DATEP.IDSA.ID 上没有覆盖索引,这将导致对这些谓词进行表扫描 上述列的索引不是最佳的(包括太多列等) 当您的查询可能受益于并行性时,它是串行的。 在不区分大小写排序规则的数据库上使用 LOWER 函数(大多数情况下不区分,尽管此函数不会使事情变慢很多) 缓存中有错误的查询计划。尝试在底部添加OPTION (RECOMPILE),这样you get a new query plan。在比较两个查询的速度时也会这样做,以确保它们没有使用缓存计划,或者当另一个查询不使用缓存计划时,这会导致结果出现偏差。

由于您的查询超时,请try including the estimated execution plan 并将其发布到past the plan

【讨论】:

你是对的,执行计划隐藏了子树的成本。事实证明,将 2 个存在放在一起可以使所需的处理能力猛增,并使计划的完成时间增加近 50 倍。 然后我只做一个到临时,然后做另一个或适当地调整。 我发布了这个问题的答案,但我发现工会是最有效的解决方案 这是一种保证【参考方案2】:

我发现将 2 EXISTS 放入 WHERE 条件会使整个过程花费更长的时间。我发现修复它的是使用UNION 并将EXISTS 保留在单独的查询中。最终结果如下所示:

SELECT *
FROM table1 S    
WHERE
--Statement 1
   EXISTS 
   (
      SELECT 1
      FROM table2 P WITH (NOLOCK)
         INNER JOIN table3 SA ON SA.ID = P.ID
      WHERE P.DATE = @Date AND P.OTHER_ID = S.ID
      AND 
      (
         SA.FILTER = ''
         OR 
         (
             SA.FILTER = 'bar'
             AND 
             LOWER(S.OTHER) = 'foo'
          )
       )
   )

UNION

--Statement 2 
SELECT *
FROM table1 S    
WHERE
   EXISTS 
   (
      SELECT 1
      FROM table4 P WITH (NOLOCK)
         INNER JOIN table5 SA ON SA.ID = P.ID
      WHERE P.DATE = @Date 
            AND P.OTHER_ID = S.ID 
            AND LOWER(S.OTHER) = 'foo'
    )

【讨论】:

以上是关于为啥多个 EXISTS 会破坏查询的主要内容,如果未能解决你的问题,请参考以下文章

具有多个 EXISTS 的查询返回太多行

带有“exists”子句和多个表的 SQL 子查询

为啥 Spring 批处理多线程步骤会破坏 ItemProcessor 中的其他存储库休眠查询?

为啥这个查询不起作用(EXISTS 的问题)

为啥这个 IF NOT EXISTS 语句不起作用?

为啥 oracle 不支持在单个查询中更新多个表?