SQL Server条件流

如果我在
IF EXISTS
条件下写两个
SELECT
语句,并且在这些选择查询之间有一个
AND
子句,即使第一个
SELECT
返回false,两个查询都会被执行吗?
IF EXISTS (SELECT....) AND EXISTS(SELECT ....)
BEGIN

END
在这种情况下,SQL Server引擎是否同时执行SQL语句? 谢谢 Krish     
已邀请:
我会把测试重写为
IF CASE
     WHEN EXISTS (SELECT ...) THEN CASE
                                   WHEN EXISTS (SELECT ...) THEN 1
                                 END
   END = 1  
这可以保证此处所述的短路,但这意味着您需要选择最便宜的一个进行预先评估,而不是将其留给优化器。 在我极为有限的测试中,以下似乎在测试时也适用 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

*/
所有这些的计划看起来非常相似。
SELECT 1 WHERE ...
版本和
IF ...
版本之间行为差异的原因是,对于前者,如果条件为假,则正确的行为是不返回结果,因此它只链接
OUTER SEMI JOINS
,如果一个为假,则零行携带转到下一个。 但是,
IF
版本总是需要返回1或0的结果。此计划在其外连接中使用探测列,如果未通过
EXISTS
测试(而不是简单地丢弃该行),则将其设置为false。这意味着总有一行进入下一个Join并且它总是被执行。
CASE
版本有一个非常相似的计划,但它使用一个
PASSTHRU
谓词,如果不满足之前的
THEN
条件,它用于跳过JOIN的执行。我不确定为什么组合
AND
s不会使用相同的方法。 2.20ѭ
EXISTS OR  EXISTS
版本使用连接(
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
*/
3.添加
ELSE
我确实尝试过De Morgan定律将
AND
转换为
OR
,看看是否有任何区别。转换第一个查询给出
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
*/
因此,这对短路行为仍然没有任何影响。但是,如果你移除
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
*/
    
我相信你可以依赖大多数(如果不是全部)现代语言的IF语句的短路行为。您可以先测试一个真实条件,然后用
1/0
替换第二个条件进行测试,如果没有发生短路,这会给你一个零除错误,如下所示:
IF 1>0 OR 1/0 BEGIN
  PRINT 'Short Circuited'
END
如果你不相信,你总是可以重写你的查询来做到这一点:
IF EXISTS(SELECT...) BEGIN
  IF EXISTS(SELECT...) BEGIN
    ...
  END
END
    
如果我使用AND执行查询,即使这样,也会访问这两个表 SET STATISTICS IO ON IF EXISTS(SELECT * from master..spt_values where [name] ='rpcc')和EXISTS(SELECT * from master..spt_monitor where pack_sent = 5235252)PRINT'Y' 表'spt_monitor'。扫描计数1,逻辑读取1,物理读取0,预读取读取0,lob逻辑读取0,lob物理读取0,lob预读读取0。 表'spt_values'。扫描计数1,逻辑读取17,物理读取0,预读取读取0,lob逻辑读取0,lob物理读取0,lob预读读取0。     
我在sqlteam上的以下博客条目中引用了以下引用: SQL Server如何短路WHERE条件评估   当感觉像它时,它会发生,但不会像你想象的那样。      作为开发人员,您必须意识到SQL Server不会像在其他编程语言中那样进行短路操作,并且您无法强制执行任何操作。 有关详细信息,请查看上述博客条目中的第一个链接,该链接指向另一个博客: SQL Server短路吗?   最终的判决?好吧,我还没有,但是可以肯定地说,只有当你在CASE表达式中表达多个WHEN条件时才能确保特定的短路。使用标准布尔表达式,优化器将根据您查询的表,索引和数据在其认为合适的情况下移动。     
有一个有趣的观察。我有两个表tbla和tblb。 tbla有一个主键(idvalue),用作tblb中的外键。两者都有一行idvalue = 1,但没有idvalue为-1的行。现在,下面的查询只使用一个表
select 1
where exists
(select 1 from tbla where idvalue = -1)
and exists (select 1 from tblb where idvalue= 1)
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'tbla'. Scan count 0, logical reads 3, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
这是显而易见的,因为优化器知道由于存在主键 - 外键关系,因此如果tbla中缺少该值,则它永远不会出现在tblb中。因此,优化器将决定运行时不需要在tblb上进行搜索。 但是,如果我将查询写为
select 1
where exists
(select 1 from tbla where idvalue = 1)
and exists (select 1 from tblb where idvalue= -1)
然后访问这两个表。这很明显,因为优化器知道它必须检查两个位置以确保满足AND条件。 但是,在这两种情况下,实际执行计划都显示了对tbla和tblb的搜索。这对我来说很奇怪。有什么想法吗?     
不。 我刚刚在SQL Server 2008中测试过,如果第一次评估失败,它会立即跳过
IF
块。 这很容易测试。 对于你的第一次评估做一些像
IF 1=0
和你的第二次做任何事情,然后显示实际的执行计划。在我的作品中,它只进行常数扫描来评估这些常数。     
您可以通过执行以下操作来阻止第二次扫描:
declare @test bit
select @test = case when exists(select 1...) then 1 else 0 end
if @test = 1
begin
    --1st test passed
    select @test = case when exists(select 2...) then 1 else 0 end
end
if @test = 1
begin
    print 'both exists passed'
end
    

要回复问题请先登录注册