SQL中的WHERE语句是否会进行短路求值?

171
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 AND @key = t.Key
如果不会,为什么?
如果会,是否有保障?这是ANSI SQL的一部分还是特定于数据库的?
如果是数据库特定的,是SQLServer?Oracle?MySQL?

11
@splender - 取决于问题的答案。 - Greg Dean
17
@Michael和@spender - 问题的重点是第二个条件是否被评估。问题的重点不在于这个特定的SQL语句是否以尽可能少的字符编写。对于更复杂的例子,这无疑很重要,因为如果where子句短路,您可以编写在其他情况下会出错的表达式。 - Greg Dean
如果还有人关心的话:在MySQL Server 5.1.36-community中,SELECT (value IS NULL) OR (value IS NOT NULL AND value='something')不会返回任何行。 - Ast Derek
请记住,SQL是一种声明性语言,您不需要定义如何完成某件事。您只需描述您想要的结果即可。 - Stefan Steinegger
2
短路计算意味着从左到右评估条件。例如给定一个条件 WHERE a = 1 AND b = 2,对于数据库引擎来说,先查找所有 b = 2 的行,然后再过滤 a = 1 可能更有效率。但如果要求保证,则优化器将变得无用。 - Salman A
显示剩余3条评论
16个回答

84

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

6.3.3.3 规则评估顺序

[...]

如果优先级不是由格式或括号确定的话,表达式的有效计算通常从左到右进行。然而,当操作数或运算符可能导致条件被触发,或者如果表达式的结果可以在不完全计算所有部分的情况下确定时,则实际上是否按照从左到右的顺序计算表达式取决于具体实现。


7
实现相关?好的,也很好知道。至少“CASE”会被短路。 - dakab
6
这是否意味着表达式计算是不明确的?"(0=0 OR NULL)",如果全部项都被评估,结果始终为NULL,但如果从左到右进行评估并短路,则始终为true。 - user48956
10
SQL是一种声明式语言,它基本上表达了计算逻辑而不描述其控制流;这与短路求值的命令式风格及其后果有些相悖。 - Jorge Garcia
1
我没有像@JorgeGarcia那样考虑过。我猜短路评估隐含地强制了操作的顺序。我正在处理一些可能与此有关的微妙问题的代码。感谢您的洞察力。 - Carnot Antonio Romero
1
@user48956 0 = 0 或 null = true 或 null = true,无论是否短路,因为 null 在语义上等同于 unknown,而在这个析取式中的 unknowntrue 还是 false 并不重要,因为 true or falsetruetrue or true 也是 truenull or 0 = 0 也是 true - allmhuran

67

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

如果您需要它,我建议使用Case语句:

Where Case when Expr1 then Expr2 else Expr3 end = desiredResult

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


3
这取决于RDBMS的实现,我想。至少对于SQL Server,存在至少一个例外情况,据文档记录,不显示此行为(即短路);参见CASE (Transact-SQL) - Remarks。我在这个答案中引用了这个案例,回答了关于Sql - WHERE条件的显式顺序?的问题。 - TT.
1
表达式要用Case,而不是语句。 - jarlh

19

我认为这是一种情况,即使它不短路,我也会这样写,原因有三。

  1. 对于MSSQL,它不是通过在显然的位置查看BOL来解决的,所以对我来说,这使它具有规范上的歧义。

  2. 因为至少我知道我的代码将会起作用。更重要的是,那些接替我的人也是如此,所以我不会让他们反复担心同样的问题。

  3. 我经常为多个DBMS产品编写代码,并且如果可以轻松地避开它们,就不想记住差异。


4
好的建议。虽然它没有回答问题,但这是一个很好的实用观点。所以+1。 - Greg Dean
同意。但有趣的是,我注意到Entity Framework会保留短路逻辑语句。不确定这是否是一个潜在的错误原因,或者他们是否知道我们不知道的SQL服务器的某些东西。 - xr280xr

12

我不相信在 SQL Server(2005)中短路运算是可以保证的。SQL Server 会通过其优化算法来运行您的查询,该算法考虑了许多因素(索引、统计信息、表大小、资源等),以得出一个有效的执行计划。在此评估之后,您无法确保您的短路逻辑是可靠的。

我曾经遇到过同样的问题,我的研究并没有给我一个明确的答案。您可以编写一个小查询来验证它是否有效,但是当您的数据库负载增加、表变得更大,并且数据库中的内容被优化和更改时,您能确定结论仍然成立吗?我不能,因此我选择谨慎起见,在 WHERE 子句中使用 CASE 来确保短路。


7

您需要记住数据库的工作原理。给定一个参数化查询,数据库会根据该查询构建执行计划,而不考虑参数的值。无论实际提供的值是什么,每次运行查询时都会使用此查询。查询是否在某些值下短路对执行计划没有影响。


6
它对执行速度很重要! - user4951
仅仅因为当前的工作方式是这样,并不意味着它不能被改变。我们必须将模型/语义与实现分开。执行计划在内部实现,以优化查询的执行...而短路语义不仅违背了SQL的声明性本质,还可能限制这种优化。然而,如果DBMS支持短路评估语义,执行计划的实现将会改变以支持这种语义。 - Jorge Garcia

5
我通常将其用于可选参数。这与短路有关吗?
SELECT  [blah]
FROM    Emp
WHERE  ((@EmpID = -1) OR (@EmpID = EmpID))

这使我可以选择传入-1或其他值来考虑对属性的可选检查。有时需要连接多个表,或者最好使用视图。

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


有趣的是,对我来说,这似乎在MySQL 8中开始无法工作。 - Matthew Lock

4

2
对于SQL Server,我认为这取决于版本,但是我的经验是,对于SQL Server 2000,即使@key为空,它仍然评估@key = t.Key。换句话说,在评估WHERE子句时,它不会进行有效的短路处理。
我看到有人推荐像您的示例一样的结构作为执行灵活查询的方法,用户可以输入或不输入各种条件。我的观察是,当@key为空时,Key仍然参与查询计划,如果Key被索引,则不会有效地使用该索引。
这种具有不同标准的灵活查询可能是动态创建SQL确实是最好的方式之一。如果@key为空,则根本不将其包括在查询中。

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实现使用短路评估,它只能希望加快查询执行速度。


1
是的,布尔运算符是可交换的。我认为对象(或不是)与此无关。 - Greg Dean

1

我不知道短路运算,但我会将其写成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


1
有人能证实右边的变量吗?出于某种原因,我很难相信它。 - Greg Dean
目前无法找到其他更多的资料。 - DForck42
据我理解这篇文章是在讨论列名上的函数不可搜索。我理解这一点。然而,我认为(A = @a)或者(@a = A)并不重要。 - Greg Dean
我可能是错的。如果这个问题还不存在,那么这可能是一个好问题。 - DForck42

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接