- 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)
上述查询在三秒钟内完成.
如果上面的查询返回任何值,我们希望存储过程为EXIT,所以我重写如下:
- 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
然而这需要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',1)
- Return
- End
上面重写的问题是上面的查询是更大的存储过程的一部分,它返回多个结果集.在C#中,我们遍历每个结果集并进行一些处理.
上面返回一个空的结果集,所以如果我采用这种方法,我必须改变我的C#并再次进行部署.
所以我的问题是,
why does using just
IF EXISTS
changes the plan to take so much time?
以下是可能对您有所帮助的详细信息,如果您需要任何详细信息,请告知我们:
>创建表和统计信息脚本以获得与我的相同的计划
>慢执行计划
>快速执行计划
Slow plan using Brentozar Paste the plan
Fast Plan using Brentozar Paste the plan
注意:两个查询都是相同的(使用参数),唯一的区别是EXISTS(我可能在匿名时犯了一些错误).
表创建脚本如下:
http://pastebin.com/CgSHeqXc – 小桌子统计
http://pastebin.com/GUu9KfpS – 大表统计
解决方法
As a final example,consider that a logical semi-join (such as a
sub-query introduced with EXISTS) shares the overall theme: it should
be optimised to find the first matching row quickly.
在您的查询中,这显然会引入嵌套循环并删除并行性,从而导致计划速度变慢.
因此,您可能需要找到一种方法来重写查询,而无需使用查询中的NOT EXISTS.
您可能会使用LEFT OUTER JOIN重写您的查询并通过测试NULL检查smalltable中没有行
- 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
- )
您也可以使用EXCEPT查询,具体取决于您需要比较的字段数,如下所示:
- If EXISTS(
- SELECT source
- FROM somedb.dbo.bigtable l
- WHERE databasename = 'someval'
- AND source <> 'kt'
- EXCEPT
- SELECT source
- FROM dbo.smalltable
- )
请注意,Aaron Bertrand有一篇博文providing reasons why he prefers NOT EXISTS,您应该阅读以了解其他方法是否更好,并了解NULL值的潜在正确性问题.
相关Q&答:IF EXISTS taking longer than embedded select statement