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

例如:

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的?

参考: 短路评估

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

6.3.3.3规则评估顺序

[…]

如果优先顺序不是由格式或括号决定的,则expression式的有效评估通常从左到右进行。 但是, 实现依赖于expression式是否实际上从左到右进行计算,特别是在操作数或运算符可能会导致条件被提出的情况下, 或者如果可以在不完全评估expression式的所有部分的情况下确定expression式的结果。

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

如果你需要,我build议一个案例声明:

 Where Case when Expr1 then Expr2 else Expr3 end = desiredResult 

Expr1总是被评估,但是Expr2Expr3只有一个会被每行计算。

我认为这是我写作的一种情况,好像没有短路一样,原因有三。

  1. 因为对MSSQL来说,在显而易见的地方看BOL并不能解决问题,所以对于我来说,这样做是不正确的。

  2. 因为至less我知道我的代码将工作。 更重要的是,那些追随我的人也是如此,所以我不会让他们一次又一次地通过同样的问题来担心。

  3. 我经常写几个DBMS产品,如果能够轻松地解决它们,我不想记住这些差异。

我不相信SQL Server(2005)中的短路是有保证的。 SQL Server通过优化algorithm运行你的查询,该algorithm考虑了很多事情(索引,统计,表格大小,资源等),以提出一个有效的执行计划。 在评估之后,你不能确定你的短路逻辑是有保证的。

我前一段时间遇到了同样的问题,我的研究并没有给我一个明确的答案。 你可以写一个小的查询给你一个certificate它的工作原理的感觉,但是你可以肯定,随着数据库的负载增加,表变得越来越大,事情在数据库中得到优化和改变,这个结论将会保持。 我不能,因此在谨慎的方面犯了错误,并在WHERE子句中使用CASE来确保短路。

你必须记住数据库是如何工作的。 给定一个参数化查询,数据库build立一个执行计划基于该查询没有参数的值。 每次查询运行时都会使用此查询,而不pipe实际提供的值是什么。 具有一定数值的查询短路对执行计划无关紧要。

我通常使用这个可选参数。 这与短路相同吗?

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

这使我可以select传入-1或其他值来说明属性的可选检查。 有时这涉及到多个表,或者最好是一个视图。

非常方便,不完全确定它给数据库引擎的额外工作。

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

SQL Server可以自由地在任何她认为合适的地方优化查询,所以在博客文章中给出的例子中,不能依赖短路。

然而,CASE显然是以书面的顺序进行评估 – 查看该博客的评论。

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

我曾经看到有人推荐一个类似于你的例子的结构作为一种灵活的查询方式,用户可以input或不input各种标准。 我的观察是,当@key为空时,Key仍然参与查询计划,如果Key被索引,那么它不会有效地使用索引。

这种具有不同标准的灵活查询可能是dynamic创build的SQL真的是最好的方法。 如果@key为null,那么根本就不包含它在查询中。

我不知道短暂的循环,但是我会把它写成if-else语句

 if (@key is null) begin SELECT * FROM Table t end else begin SELECT * FROM Table t WHERE t.Key=@key end 

variables也应该总是在方程的右边。 这使得它是可靠的。

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

短路评估的主要特点是一旦确定结果就停止评估expression式。 这意味着其余的expression式可以被忽略,因为无论它是否被评估,结果都是相同的。

二元布尔运算符是可比的,这意味着:

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

所以评估的顺序是不能保证的。 评估顺序将由查询优化器确定。

在使用对象的语言中,可能会出现可以编写布尔expression式的情况,只能通过短路评估来评估布尔expression式。 您的示例代码构造经常用于这样的语言(C#,Delphi,VB)。 例如:

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

这个C#示例会导致exception,如果someString == null因为它将被充分评估。 在短路评估中,每次都会起作用。

SQL只能在不能被初始化的标量variables(没有对象)上运行,所以没有办法编写无法评估的布尔expression式。 如果你有一些NULL值,任何比较将返回false。

这意味着在SQL中,您不能编写根据使用短路或完整评估而进行不同评估的expression式。

如果SQL实现使用短路评估,则只能希望加快查询的执行速度。

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

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

这将立即返回没有logging。 出现了一种短路行为。

然后试试这个:

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

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

 (a field from table) < 0 

这花了几秒钟,表明短路的行为不再有了,每个logging都在评估复杂的操作。

希望这可以帮助家伙。

这在查询分析器中需要额外的4秒钟,所以从我所能看到的IF甚至没有被缩短…

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

有一个保证的方式将是很好的!

这里是一个演示,以certificateMySQL确实执行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故意hibernate一秒钟,每次运行时都会在日志表中添加一个条目。 以下是运行上述两个查询时logging的结果:

 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条件的左侧时,如果另一个操作数并非总是为真(由于短路),则会执行更多的慢速函数。

MS Sql服务器支持短路理论,但是显而易见的是,通过避免不必要的检查来提高性能,

支持范例:

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

在这里,第一个例子会导致错误“将转换varchar值”A“转换为数据types为int的转换失败。

当第二个条件1 = 1评估为TRUE时,第二个条件很容易运行,因此第二个条件根本不运行。

还有更多

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

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

注:我写错误的情况只是为了实现天气条件执行或短路如果错误的查询结果意味着执行的条件,短路其他情况。

简单的解释

考虑,

 WHERE 1 = 1 OR 2 = 2 

作为第一个条件被评估为TRUE ,因为它对任何值的评估根本不会影响结果,所以它对于评估第二个条件没有意义,所以Sql Server通过跳过不必要的条件检查或评估来节省查询执行时间的好机会。

“OR”的情况下,如果将第一个条件评估为TRUE,则通过“OR”连接的整个链将被认为评估为真,而不评估其他条件。

 condition1 OR condition2 OR ..... OR conditionN 

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

考虑第二个条件

 WHERE 1 = 0 AND 1 = 1 

作为第一个条件得到评估为FALSE无意义的评估第二个条件,因为它的任何价值的评估根本不会影响结果,所以它再次是Sql Server通过跳过不必要的条件检查或评估保存查询执行时间的好机会。

“AND”的情况下,如果将第一个条件评估为FALSE,则与“AND”连接的整个链将被视为评估为FALSE而不评估其他条件。

 condition1 AND condition2 AND ..... conditionN 

如果条件1的计算结果为FALSE ,则将所有条件保留到条件 N将被跳过。 在确定第一个FALSE时的广义词汇中,由AND链接的所有其他条件将被跳过。

因此,一个明智的scheme应该总是以这样一种方式来规划条件链,那么首先评估昂贵的或最不利的条件,或者以这种方式来规划可以获得最大的短路效益的条件