SQLWHERE 子句是否短路?

是否对 SQL WHERE 子句中的布尔表达式 < a href = “ http://en.wikipedia.org/wiki/Short-Circuit _ value”rel = “ norefrer”> short-Circuit 进行了计算 ?

例如:

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 AND @key = t.Key

如果没有,为什么不呢?

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

如果特定于数据库,SQLServer? Oracle? MySQL?

60505 次浏览

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

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

这种具有不同条件的灵活查询可能是动态创建 SQL 的最佳方式之一。如果@key 为 null,那么您根本不需要在查询中包含它。

我不认为 SQLServer (2005)中的短路是有保证的。SQLServer 通过其优化算法运行您的查询,该算法考虑了很多因素(索引、统计信息、表大小、资源等) ,从而得出有效的执行计划。在这个评估之后,你不能肯定地说你的短路逻辑是有保证的。

不久前我自己也遇到了同样的问题,我的研究并没有给我一个明确的答案。您可以编写一个小查询来证明它是有效的,但是您能够确保随着数据库负载的增加,表变得更大,并且数据库中的内容得到优化和更改,这个结论将成立。我不能,因此出于谨慎,我在 WHERE 子句中使用了 CASE 来确保短路。

我认为这是其中一种情况下,我会写它,如果它没有短路,有三个原因。

  1. 因为对于 MSSQL 来说,它不是通过在显而易见的地方查看 BOL 来解决的,所以对我来说,这使得它在规范上是模棱两可的。

  2. 因为至少我知道我的代码可以工作。更重要的是,那些追杀我的人也会这么做,所以我不会让他们一遍又一遍地担心同一个问题。

  3. 我经常为几个 DBMS 产品编写代码,如果可以很容易地解决这些差异,我就不想记住它们。

短路计算的主要特点是计算结果一旦确定就停止计算。这意味着可以忽略表达式的其余部分,因为不管计算与否,结果都是相同的。

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

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 实现使用短路计算,那么它只能加快查询的执行。

我不知道什么短路,但我会把它写成 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

您必须记住数据库是如何工作的。给定一个参数化查询,db 基于该查询生成一个执行计划,但不包含参数值。无论实际提供的值是什么,每次运行查询时都会使用此查询。具有某些值的查询是否短路与执行计划无关。

我通常使用这个可选参数。这是否等同于短路?

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

这使我可以选择传入 -1或其他任何东西来说明可选的属性检查。有时,这涉及到在多个表上联接,或者最好是在一个视图上联接。

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

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

SET @ADate = NULL


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

如果有一个有保障的方式就好了!

ANSI SQL 草案20035WD-01-Framework-2003-09. pdf

6.3.3.3规则评估顺序

[...]

优先级不是由格式决定还是由 括号,表达式的有效计算通常是 从左到右执行 实现相关 表达式是否实际从左到右求值,特别是当操作数或运算符可能 导致条件升高 < strong > ,或者如果表达式的结果 可以确定,而无需完全评估 表情

下面是 SQLServer2008R2上的一个快速测试:

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

这花了几秒钟的时间,表明短路行为不再存在,复杂的操作正在为每个记录进行评估。

希望这对你们有帮助。

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

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

然而,一个案例显然是文件评估的书面顺序-检查该博客帖子的评论。

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

如果你需要的话,我建议一个案例陈述:

Where Case when Expr1 then Expr2 else Expr3 end = desiredResult

总是计算 Expr1,但是每行只计算 Expr2Expr3中的一个。

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

支持例子:

SELECT 'TEST'
WHERE 1 = 'A'


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

在这里,第一个示例将导致错误“在将 varchar 值‘ A’转换为数据类型 int 时转换失败”

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

还有呢

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

在这里,第一个条件的计算结果是 false,因此数据库管理系统将执行第二个条件,并且您将再次得到如上例所示的转换错误。

注意: 我写这个错误的条件只是为了了解条件是执行还是短路 如果错误的查询结果意味着执行条件,则短路。

简单的解释

想想看,

WHERE 1 = 1 OR 2 = 2

因为第一个条件的求值是 没错,所以对第二个条件的求值是没有意义的,因为它的求值是以任何值来计算的 不会对结果造成任何影响,因此对于 Sql Server 来说,跳过不必要的条件检查或计算来节省查询执行时间是一个很好的机会。

“或”的情况下,如果第一个条件被评估为 没错,那么通过 “或”连接的整个链将被认为是被评估为真,而没有评估其他条件。

condition1 OR condition2 OR ..... OR conditionN

如果条件1被赋值为 true,则停止所有条件,直到条件 N 被跳过。 在确定第一个 没错的广义词中,通过 OR 链接的所有其他条件将被跳过。

考虑第二个条件

WHERE 1 = 0 AND 1 = 1

当第一个条件被求值到 假的时,对第二个条件的求值就没有意义了,因为它的求值是以任何值计算的 不会对结果造成任何影响,因此对于 Sql Server 来说,跳过不必要的条件检查或计算,从而节省查询执行时间的好机会再次出现。

“然后”的情况下,如果第一个条件被评估为 假的,那么与 “然后”连接的整个链将被认为被评估为 FALSE,而不评估其他条件。

condition1 AND condition2 AND ..... conditionN

如果条件1被赋值为 假的,则停止所有条件,直到跳过 条件 N。 在确定第一个 假的的广义词中,还有链接的所有其他条件都将被跳过。

因此,一个聪明的程序员应该总是用这样一种方式来编写条件链,比较便宜的或者排除条件最先得到评估, 或者以一种能够最大限度地利用短路的方式安排条件

这里有一个演示来证明 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 条件的左边时,如果另一个操作数并不总是为真(由于短路) ,那么它会被执行更多次。

快速的答案是: “短路”行为是没有文档的实现。 这里有一篇非常好的文章来解释这个主题。

了解 T-SQL 表达式短路