是否对SQL WHERE子句进行了短路评估?

时间:2009-04-25 16:11:13

标签: sql short-circuiting

例如:

SELECT * 
FROM Table t 
WHERE @key IS NULL OR (@key IS NOT NULL AND @key = t.Key) 

如果 @key IS NULL 的计算结果为true,那么 @key IS NOT NULL并且@key = t.Key 评估了吗?

如果不是,为什么不呢?

如果是,是否有保证?它是ANSI SQL的一部分还是特定于数据库?

如果特定于数据库,SqlServer?甲骨文? MySQL的?

参考:Short Circuit Evaluation

15 个答案:

答案 0 :(得分:66)

ANSI SQL Draft 2003 5WD-01-Framework-2003-09.pdf

  

6.3.3.3规则评估顺序

     

[...]

     

优先级不是由格式决定的   括号,表达式的有效评价一般   从左到右执行。但是,确实如此   依赖于实现表达式是否实际上是从左到右计算的,特别是在操作数或运算符可能时   引起条件或表达式的结果   可以在不完全评估的所有部分的情况下确定   表达

答案 1 :(得分:57)

从上面来看,短路并不是真的可用。

如果您需要,我建议使用Case声明:

Where Case when Expr1 then Expr2 else Expr3 end = desiredResult

Expr1始终会被评估,但每行只会评估Expr2Expr3中的一个。

答案 2 :(得分:18)

我认为这是我写它的情况之一,好像它没有发生短路,原因有三个。

  1. 因为对于MSSQL来说,通过在显而易见的位置查看BOL无法解决问题,所以对我而言,这会使其规范化。

  2. 因为至少我知道我的代码会起作用。更重要的是,那些追随我的人也是如此,所以我不会让他们一遍又一遍地担心同样的问题。

  3. 我经常为几个DBMS产品写作,如果我能轻松解决这些问题,我不想记住差异。

答案 3 :(得分:12)

我不相信SQL Server(2005)中的短路是有保证的。 SQL Server通过其优化算法运行您的查询,该算法考虑了很多事情(索引,统计信息,表大小,资源等)以提出有效的执行计划。在此评估之后,您无法确定您的短路逻辑是否得到保证。

我前段时间遇到了同样的问题,我的研究确实没有给出明确的答案。你可以编写一个小的查询,让你有一个证据证明它是有效的,但是你可以确定随着数据库负载的增加,表变得更大,事情在数据库中得到优化和改变,结论将是保持。我不能因此而谨慎,并在WHERE子句中使用CASE来确保短路。

答案 4 :(得分:7)

您必须记住数据库的工作方式。给定参数化查询,db基于该查询构建执行计划,而不使用参数值。无论实际提供的值是什么,每次运行查询时都会使用此查询。具有特定值的查询短路是否与执行计划无关。

答案 5 :(得分:3)

我通常将此用作可选参数。这与短路相同吗?

SELECT  [blah]
FROM    Emp
WHERE  ((@EmpID = -1) OR (@EmpID = EmpID))

这使我可以选择传入-1或其他任何因素来考虑属性的可选检查。有时这涉及连接多个表,或者最好是视图。

非常方便,不完全确定它为db引擎提供的额外工作。

答案 6 :(得分:2)

对于SQL Server,我认为这取决于版本,但我对SQL Server 2000的体验是,即使@key为null,它仍会评估@key = t.Key。换句话说,在评估WHERE子句时,它不会进行有效的短路。

我见过人们推荐像您的示例这样的结构作为一种灵活查询的方式,用户可以输入或不输入各种条件。我的观察是当@key为null时Key仍然参与查询计划,如果Key被索引,那么它不会有效地使用索引。

这种具有不同标准的灵活查询可能是一种情况,其中动态创建的SQL确实是最好的方法。如果@key为null,那么根本不将它包含在查询中。

答案 7 :(得分:2)

我不知道短暂的马戏,但我会把它写成if-else语句

if (@key is null)
begin

     SELECT * 
     FROM Table t 

end
else
begin

     SELECT * 
     FROM Table t 
     WHERE t.Key=@key

end

此外,变量应始终位于等式的右侧。这让它很难受。

http://en.wikipedia.org/wiki/Sargable

答案 8 :(得分:2)

只是偶然发现了这个问题,并且已经找到了这个博客条目:http://rusanu.com/2009/09/13/on-sql-server-boolean-operator-short-circuit/

SQL服务器可以自由地在她认为合适的任何地方优化查询,因此在博客文章中给出的示例中,您不能依赖于短路。

然而,CASE显然是以书面形式进行评估 - 检查该博客文章的评论。

答案 9 :(得分:1)

短路评估的主要特征是一旦确定结果就停止评估表达式。这意味着可以忽略表达式的其余部分,因为无论是否进行评估,结果都是相同的。

二进制布尔运算符是可交换的,意思是:

a AND b == b AND a
a OR  b == b OR  a
a XOR b == b XOR a

因此无法保证评估顺序。评估顺序将由查询优化器确定。

在具有对象的语言中,您可以编写可以仅通过短路评估进行评估的布尔表达式。您的示例代码构造通常用于此类语言(C#,Delphi,VB)。例如:

if(someString == null | someString.Length == 0 )
  printf("no text in someString");

这个C#示例将导致someString == null异常,因为它将被完全评估。在短路评估中,它每次都会工作。

SQL仅对无法未初始化的标量变量(无对象)进行操作,因此无法编写无法计算的布尔表达式。如果您有一些NULL值,任何比较都将返回false。

这意味着在SQL中,您无法根据使用短路或完整评估来编写不同评估的表达式。

如果SQL实现使用短路评估,它只能加速查询执行。

答案 10 :(得分:1)

在SQL Server 2008 R2上进行快速而肮脏的测试:

SELECT *
FROM table
WHERE 1=0
AND (function call to complex operation)

立即返回,没有记录。存在一种短路行为。

然后尝试了这个:

SELECT *
FROM table
WHERE (a field from table) < 0
AND (function call to complex operation)

知道没有记录会满足这个条件:

(a field from table) < 0

这需要几秒钟,表明短路行为不再存在,并且正在评估每条记录的复杂操作。

希望这有帮助的人。

答案 11 :(得分:1)

这是一个演示来证明 MySQL确实执行WHERE子句短路

http://rextester.com/GVE4880

这将运行以下查询:

SELECT myint FROM mytable WHERE myint >= 3 OR myslowfunction('query #1', myint) = 1;
SELECT myint FROM mytable WHERE myslowfunction('query #2', myint) = 1 OR myint >= 3;

这些之间的唯一区别是OR条件下操作数的顺序。

myslowfunction故意睡眠一秒,并且每次运行时都会在日志表中添加一个条目。以下是运行上述两个查询时记录的结果:

myslowfunction called for query #1 with value 1
myslowfunction called for query #1 with value 2
myslowfunction called for query #2 with value 1
myslowfunction called for query #2 with value 2
myslowfunction called for query #2 with value 3
myslowfunction called for query #2 with value 4

上面显示当一个慢速函数出现在OR条件的左侧时,如果另一个操作数不是真的(由于短路),它会被执行多次。

答案 12 :(得分:0)

在查询分析器中需要额外4秒,所以我可以看到IF甚至没有短路......

SET @ADate = NULL

IF (@ADate IS NOT NULL)
BEGIN
    INSERT INTO #ABla VALUES (1)
        (SELECT bla from a huge view)
END

有保证的方式会很好!

答案 13 :(得分:-1)

快速回答是:“短路”行为是未记录的实现。 这是一篇很好的文章,解释了这个主题。

Understanding T-SQL Expression Short-Circuiting

答案 14 :(得分:-3)

很明显,MS Sql server支持短路理论,通过避免不必要的检查来提高性能,

支持示例:

SELECT 'TEST'
WHERE 1 = 'A'

SELECT 'TEST'
WHERE 1 = 1 OR 1 = 'A'

此处,第一个示例会导致错误&#39;转换varchar值时转换失败&#39; A&#39;数据类型为int。&#39;

虽然第二个条件很容易运行,因为条件1 = 1被评估为TRUE,因此第二个条件根本没有运行。

更多

SELECT 'TEST'
WHERE 1 = 0 OR 1 = 'A'

这里第一个条件将评估为false,因此DBMS将进入第二个条件,您将再次获得转换错误,如上例所示。

注意:我的错误条件只是为了实现天气条件的执行或短暂的循环 如果错误中的查询结果表示执行的条件,其他方式是短暂的。

简单说明

考虑,

WHERE 1 = 1 OR 2 = 2

因为第一个条件是评估为 TRUE ,评估第二个条件是没有意义的,因为它的评估无论什么价值 根本不会影响结果,因此Sql Server有机会通过跳过不必要的条件检查或评估来节省查询执行时间。

如果&#34; OR&#34; 如果第一个条件评估为 TRUE 整个链由&#34; OR&#34连接; 在没有评估其他人的情况下被视为评估为真。

condition1 OR condition2 OR ..... OR conditionN

如果条件1被评估为真,则休息所有条件,直到条件N被跳过。 在确定第一个 TRUE 的广义词中,将跳过由OR链接的所有其他条件。

考虑第二个条件

WHERE 1 = 0 AND 1 = 1

因为第一个条件是被评估为 FALSE ,评估第二个条件毫无意义,因为它的评估无论什么价值 根本不会影响结果,所以再次为Sql Server提供了通过跳过不必要的条件检查或评估来节省查询执行时间的好机会。

如果&#34; AND&#34; 如果第一个条件评估为 FALSE 整个链与&#34; AND&#相关联34; 在没有评估其他人的情况下被视为评估为FALSE。

condition1 AND condition2 AND ..... conditionN

如果条件1被评估为 FALSE ,则休息所有条件,直到 conditionN 被跳过。 在确定第一个 FALSE 的广义词汇中,将跳过由 AND 链接的所有其他条件。

因此,一个聪明的程序员应该总是按照这样的方式编写条件链,这种方式很少,或者说消除了最大的消除条件,首先得到评估, 或以这种方式安排可以获得最佳短路效果的条件