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


142

是否在SQL WHERE子句中布尔表达式进行短路求值

例如:

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

如果@key IS NULL的计算结果为true,则@key不是NOT AND @key = t.Key的计算结果吗?

如果否,为什么不呢?

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

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


@key子句不是NOT NULL冗余吗?LHS上的@key IS NULL子句可以解决这个问题吗?
支出者

10
@splender-取决于问题的答案
Greg Dean

@格雷格:我同意消费。我看不到短路的存在与否有什么不同。如果@key为NULL,则@key = t.Key将始终返回false,因为NULL!= NULL(毕竟这就是为什么我们使用IS NULL的原因)。
Michael Madsen

14
@Michael和@spender-问题的关键是第二条件是否评估。问题的重点不是,此特定的SQL语句是否以尽可能少的字符编写。在更复杂的示例中,这无疑很重要,就像where子句短路一样,您可以编写否则可能是错误的表达式。
格雷格·迪恩

2
短路意味着从左到右评估条件。给定这样的条件,WHERE a = 1 AND b = 2对于数据库引擎来说,首先查找b = 2的所有行可能有效,然后在a = 1的位置进行过滤。如果要求保证,那么优化器将变得无用。
Salman A

Answers:


72

ANSI SQL草稿2003 5WD-01-Framework-2003-09.pdf

6.3.3.3规则评估顺序

[...]

如果优先级不是由格式或括号确定的,则通常从左到右执行表达式的有效评估。但是,取决于表达式是否实际上是从左到右求值,这 取决于实现,特别是在操作数或运算符可能导致条件升高或是否可以在不完全评估表达式所有部分的情况下确定表达式的结果时。


4
依赖于实现?大。也很高兴知道。至少CASE是短路的。
dakab 2015年

3
这不是表示表达式评估不明确吗?“(0 = 0 OR NULL)”,如果所有条件都被求值,则始终为NULL,但如果从左到右且短路则求值,则始终为true。
user48956 '16

6
SQL是一种声明性语言,它基本上表示计算的逻辑而不描述其控制流;这与短路评估的命令式及其后果相矛盾。
豪尔赫·加西亚

我没有那样想@JorgeGarcia。我猜短路评估确实隐含了对操作的命令。我正在努力处理一些代码,这可能是一个微妙问题的根源。感谢您的见解。
卡诺·安东尼奥·罗梅罗

58

综上所述,短路并不是真正可用的。

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

Where Case when Expr1 then Expr2 else Expr3 end = desiredResult

Expr1始终会被评估,但每行只会评估Expr2和中的一个Expr3


3
这取决于我假设的RDBMS的实现。至少对于SQL Server,至少存在一个异常,该异常被记录为不显示此行为(即,短路);cf CASE(Transact-SQL)-备注。我在问题Sql-WHERE条件的显式顺序中给出的答案中引用了这种情况
TT。

1
大小写表达式,而不是语句。
jarlh

19

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

  1. 因为对于MSSQL,无法通过在明显的地方查看BOL来解决它,所以对我而言,这使其模棱两可。

  2. 因为至少那时我知道我的代码可以工作。更重要的是,那些追随我的人也将如此,因此我不会让他们反复担心同一问题。

  3. 我经常为几种DBMS产品编写足够的文章,如果我能轻松解决它们之间的差异,我就不必记住它们。


4
很棒的建议。它没有回答问题,但这是一个非常实用的观点。+1
Greg Dean

12

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

不久前,我自己遇到了一个相同的问题,而我的研究确实没有给出明确的答案。您可以编写一个小的查询,以证明它可以正常工作,但是可以确定,随着数据库负载的增加,表的容量变大,并且数据库中的事物得到优化和更改,该结论将保持。我不能,因此在谨慎方面犯了错误,并在WHERE子句中使用了CASE来确保短路。


7

您必须记住数据库的工作方式。给定一个参数化查询,数据库将基于该查询构建一个执行计划,而不包含参数值。每次运行查询时都会使用此查询,而不管实际提供的值是多少。查询是否与某些值短路将与执行计划无关。


6
执行速度很重要!
user4951 2011年

仅因为它是当前的工作方式,并不意味着它不能更改。我们必须将模型/语义与实现分开。执行计划是在内部实现的,以优化查询的执行...并且短路语义不仅与SQL的声明性相矛盾,而且可能限制这种优化。但是,如果DBMS支持短路评估语义,则执行计划的实现将更改为支持此类语义。
豪尔赫·加西亚

3

我通常将此作为可选参数。这和短路一样吗?

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

这使我可以选择传入-1或任何值以考虑对属性的可选检查。有时,这涉及到多个表或最好是一个视图的联接。

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


2

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

我见过有人推荐像您的示例这样的结构,以进行灵活的查询,用户可以输入或不输入各种条件。我的观察是,当@key为null时,如果索引已编入索引,则键仍将有效地使用索引。

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



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
是的,布尔运算符是可交换的。我认为对象(或没有对象)与它无关。
格雷格·迪恩

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/可精


1
任何人都可以证实右边的变量吗?由于某些原因,我很难相信这一点。
格雷格·迪恩


据我了解,这篇文章。谈论的是列名上的函数不可存储。我了解的。但是,我认为(A = @a)或(@a = A)并不重要。
格雷格·迪恩

我可能错了。如果尚不存在,可能是一个很好的问题。
DForck42

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

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

希望对大家有帮助。


1
我的猜测是,在计划真正开始执行之前,第一个查询在编译时“短路”。
Louis Somers

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

上面显示了当另一个操作数不总是为真(由于短路)而出现在“或”条件的左侧时,慢速函数会执行多次。


4
嗯,您可能要说的是“这里是一个演示,用于证明MySQL确实在此特定实例中执行WHERE子句短路:”
TT。

1
当然-这只是证明它可能发生的证据。
史蒂夫·钱伯斯

0

在查询分析器中这需要花费额外的4秒钟,因此从我的观察中可以看到,如果连短路都没有...

SET @ADate = NULL

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

有保证的方式太好了!


-2

但是很明显,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,因此DBMS将选择第二个条件,并且再次出现上述示例中的转换错误。

注意:我编写了错误的条件,只是为了在条件被执行或短路的情况下实现,如果查询结果出错则意味着该条件已被执行,而短路则以其他方式执行。

简单说明

考虑,

WHERE 1 = 1 OR 2 = 2

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

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

condition1 OR condition2 OR ..... OR conditionN

如果condition1的值为true,则休息所有条件,直到条件N被跳过。一般而言,在确定第一个TRUE时,将跳过通过OR链接的所有其他条件。

考虑第二个条件

WHERE 1 = 0 AND 1 = 1

由于第一个条件被评估为FALSE,因此对第二个条件进行评估是没有意义的,因为以任何值进行评估都不会影响结果,因此,再次为Sql Server提供了一个很好的机会,通过跳过不必要的条件检查或评估来节省查询执行时间。

“与”的情况下,如果将第一个条件评估为FALSE,则与“与”关联的整个链条将被视为对FALSE进行评估,而不评估其他条件。

condition1 AND condition2 AND ..... conditionN

如果condition1的评估结果为FALSE,则休息所有条件,直到条件 N被跳过。一般而言,在确定第一个FALSE时,将跳过与AND链接的所有其他条件。

因此,明智的编程人员应始终以以下方式对条件链进行编程,即首先评估成本较低或消除程度最高的条件,或者以可以最大程度地受益于短路的方式来安排条件


不赞成的理由:总是使用真实的数据在真实的服务器上测试事物。似乎我以前的评论被吃掉了。
茉莉花
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.