Kri*_*ish 13 sql t-sql sql-server if-statement short-circuiting
如果我在这些select查询之间有一个子句SELECT的IF 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)
这可以保证此处所述的短路,但这意味着您需要选择最便宜的一个进行预先评估,而不是将其留给优化器.
在我极为有限的测试中,以下似乎在测试时也适用
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不会使用相同的方法.
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)
ELSE它没有想到我去尝试德·摩根定律转换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
*/
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 2008 中进行了测试,如果第一次评估失败,它会立即跳过该IF块。
这很容易测试。
对于您的第一次评估,请执行类似的操作IF 1=0,对于第二次评估,请执行任何操作,然后显示实际的执行计划。在我的例子中,它只进行常量扫描来评估这些常量。
| 归档时间: |
|
| 查看次数: |
4232 次 |
| 最近记录: |