在 IF EXISTS 中包装查询使其非常慢

The*_*war 17 performance sql-server sql-server-2012 exists query-performance

我有以下查询:

select databasename 
from somedb.dbo.bigtable l where databasename ='someval' and source  <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source)
Run Code Online (Sandbox Code Playgroud)

上述查询在三秒内完成。

如果上面的查询返回任何值,我们希望存储过程退出,因此我将其重写如下:

If Exists(
select databasename 
from somedb.dbo.bigtable l where databasename ='someval' and source  <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source)
)
Begin
Raiserror('Source missing',16,1)
Return
End
Run Code Online (Sandbox Code Playgroud)

但是,这需要 10 分钟。

我可以像下面这样重写上面的查询,它也可以在不到 3 秒的时间内完成:

  select databasename 
from somedb.dbo.bigtable l where databasename ='someval' and source  <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source
if @@rowcount >0
Begin
Raiserror('Source missing',16,1)
Return
End
Run Code Online (Sandbox Code Playgroud)

上面重写的问题是上面的查询是更大的存储过程的一部分,它返回多个结果集。在 C# 中,我们遍历每个结果集并进行一些处理。

上面返回一个空的结果集,所以如果我采用这种方法,我必须更改我的 C# 并再次进行部署。

所以我的问题是,

为什么使用 justIF EXISTS更改计划要花费这么多时间?

以下是可能对您有所帮助的详细信息,如果您需要任何详细信息,请告诉我:

  1. 创建表和统计脚本以获得与我相同的计划
  2. 慢执行计划
  3. 快速执行计划

    使用 Brentozar 的慢速计划 粘贴计划 使用 Brentozar 的
    快速计划 粘贴计划

注意:两个查询是相同的(使用参数),唯一的区别是EXISTS(虽然我在匿名时可能犯了一些错误)。

表创建脚本如下:

http://pastebin.com/CgSHeqXc -- 小桌统计
http://pastebin.com/GUu9KfpS -- 大桌统计

Tom*_*m V 19

如已经由保罗·怀特:在他的博客里面的优化:行目标在深度EXISTS介绍一排目标,即喜欢NESTED LOOPSMERGE JOIN以上HASH MATCH

作为最后一个示例,请考虑逻辑半连接(例如使用 EXISTS 引入的子查询)共享整个主题:应该对其进行优化以快速找到第一个匹配行。

在您的查询中,这显然会引入嵌套循环并删除并行性,从而导致计划变慢。

因此,您可能需要找到一种无需使用NOT EXISTSfrom查询即可重写查询的方法。

您可能会使用 a 重写查询LEFT OUTER JOIN并通过测试来检查 smalltable 中没有行NULL

If EXISTS(
    SELECT databasename
    FROM somedb.dbo.bigtable l
    LEFT JOIN dbo.smalltable c ON c.source = l.source
    WHERE databasename = 'someval'
    AND source <> 'kt'
    AND c.source IS NULL
)
Run Code Online (Sandbox Code Playgroud)

您也可以使用EXCEPT查询,具体取决于您需要比较的字段数量,如下所示:

If EXISTS(
   SELECT source
   FROM somedb.dbo.bigtable l
   WHERE databasename = 'someval'
   AND source <> 'kt'

   EXCEPT

   SELECT source
   FROM dbo.smalltable
)
Run Code Online (Sandbox Code Playgroud)

请注意,Aaron Bertrand有一篇博客文章提供了他更喜欢 NOT EXISTS 的原因,您应该通读该文章以查看其他方法是否更有效,并注意在 NULL 值的情况下潜在的正确性问题。

相关问答:IF EXISTS 比嵌入式 select 语句花费的时间更长