SQL Server条件流

Kri*_*ish 13 sql t-sql sql-server if-statement short-circuiting

如果我在这些select查询之间有一个子句SELECTIF EXISTS条件下编写两个语句,AND即使第一个SELECT返回false ,两个查询都会被执行吗?

IF EXISTS (SELECT....) AND EXISTS(SELECT ....)
BEGIN

END
Run Code Online (Sandbox Code Playgroud)

在这种情况下,SQL Server引擎是否同时执行SQL语句?

谢谢Krish

Mar*_*ith 11

我会把测试重写为

IF CASE
     WHEN EXISTS (SELECT ...) THEN CASE
                                   WHEN EXISTS (SELECT ...) THEN 1
                                 END
   END = 1  
Run Code Online (Sandbox Code Playgroud)

这可以保证此处所述的短路,但这意味着您需要选择最便宜的一个进行预先评估,而不是将其留给优化器.

在我极为有限的测试中,以下似乎在测试时也适用

1. EXISTS AND EXISTS

EXISTS AND EXISTS版本似乎最成问题.这将一些外部半连接链接在一起.在所有情况下都没有重新安排测试的顺序,试图先做更便宜的测试(这个博客文章后半部分讨论的问题).在IF ...版本中它没有任何区别,如果它没有短路.然而,当这个组合谓词被放入一个WHERE条款中时,计划会发生变化并且它短路,因此重新安排可能是有益的.

/*All tests are testing "If False And False"*/

IF EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=2)  
AND EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)=1)
PRINT 'Y'
/*
Table 'spt_values'. Scan count 1, logical reads 9
Table 'spt_monitor'. Scan count 1, logical reads 1
*/

IF EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)=1) 
AND EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=2) 
PRINT 'Y'
/*
Table 'spt_monitor'. Scan count 1, logical reads 1
Table 'spt_values'. Scan count 1, logical reads 9
*/

SELECT 1
WHERE  EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=2)  
AND EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)=1)
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_monitor'. Scan count 1, logical reads 1
*/

SELECT 1
WHERE  EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)=1) 
AND EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=2) 
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_values'. Scan count 1, logical reads 9

*/
Run Code Online (Sandbox Code Playgroud)

所有这些的计划看起来非常相似.SELECT 1 WHERE ...版本和IF ...版本之间行为差异的原因是,对于前者,如果条件为假,则正确的行为是不返回任何结果,因此它只链接OUTER SEMI JOINS,如果一个为假,则零行结转到下一个.

但是,IF版本始终需要返回1或0的结果.此计划在其外连接中使用探测列,如果EXISTS未传递测试(而不是简单地丢弃该行),则将其设置为false .这意味着总有一行进入下一个Join并且它总是被执行.

CASE版本具有非常类似的计划,但它使用PASSTHRU谓词,如果THEN不满足先前的条件,它使用它来跳过JOIN的执行.我不确定为什么组合ANDs不会使用相同的方法.

2. EXISTS OR EXISTS

EXISTS OR EXISTS版本使用concatenation(UNION ALL)运算符作为外部半连接的内部输入.这种安排意味着它可以在第一个返回时立即停止从内侧请求行(即它可以有效地短路)所有4个查询最终得到相同的计划,其中首先评估更便宜的谓词.

/*All tests are testing "If True Or True"*/

IF EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=1)  
OR EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)<>1)
PRINT 'Y'
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_monitor'. Scan count 1, logical reads 1
*/

IF EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)<>1) 
OR EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)= 1) 
PRINT 'Y'
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_monitor'. Scan count 1, logical reads 1
*/

SELECT 1
WHERE  EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)= 1)  
OR EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)<>1)
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_monitor'. Scan count 1, logical reads 1
*/

SELECT 1
WHERE  EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)<>1) 
OR EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=1) 
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_monitor'. Scan count 1, logical reads 1
*/
Run Code Online (Sandbox Code Playgroud)

3.添加 ELSE

它没有想到我去尝试德·摩根定律转换ANDOR,看看有什么不同.转换第一个查询给出

IF NOT ((NOT EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=2)  
OR NOT EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)=1)))
PRINT 'Y'
ELSE
PRINT 'N'
/*
Table 'spt_monitor'. Scan count 1, logical reads 1
Table 'spt_values'. Scan count 1, logical reads 9
*/
Run Code Online (Sandbox Code Playgroud)

因此,这对短路行为仍然没有任何影响.但是,如果您删除NOT并反转IF ... ELSE条件的顺序,它现在短路!

IF (NOT EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=2)  
OR NOT EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)=1))
PRINT 'N'
ELSE
PRINT 'Y'
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_monitor'. Scan count 1, logical reads 1
*/
Run Code Online (Sandbox Code Playgroud)


小智 7

我相信你可以依赖大多数(如果不是全部)现代语言的IF语句的短路行为.你可以尝试先测试一个真正的条件并替换你的第二个条件,1/0如果没有发生短路,就会给你一个零除错误,如下所示:

IF 1>0 OR 1/0 BEGIN
  PRINT 'Short Circuited'
END
Run Code Online (Sandbox Code Playgroud)

如果你不相信,你总是可以重写你的查询来做到这一点:

IF EXISTS(SELECT...) BEGIN
  IF EXISTS(SELECT...) BEGIN
    ...
  END
END
Run Code Online (Sandbox Code Playgroud)

  • 你**不能/**一般在SQL Server中依赖它.根据经验,优化器可以重新排列大多数事物.我会使用第二个选项来确保我想要的行为发生.这里讨论http://stackoverflow.com/questions/5195094/sql-server-predicates-lazy (3认同)

JNK*_*JNK 0

没有。

我刚刚在 SQL Server 2008 中进行了测试,如果第一次评估失败,它会立即跳过该IF块。

这很容易测试。

对于您的第一次评估,请执行类似的操作IF 1=0,对于第二次评估,请执行任何操作,然后显示实际的执行计划。在我的例子中,它只进行常量扫描来评估这些常量。