158

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?甲骨文?MySQL?

4

15 回答 15

80

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

6.3.3.3 规则评估顺序

[...]

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

于 2009-05-26T09:23:41.583 回答
64

综上所述,短路实际上是不可用的。

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

Where Case when Expr1 then Expr2 else Expr3 end = desiredResult

Expr1总是被评估,但每行只评估Expr2和之一。Expr3

于 2012-09-07T15:10:10.943 回答
19

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

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

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

  3. 我经常为几个 DBMS 产品编写代码,如果我可以轻松解决它们,我不想记住它们之间的差异。

于 2009-04-25T18:06:27.180 回答
12

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

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

于 2009-04-25T16:43:56.130 回答
7

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

于 2009-04-25T23:58:47.880 回答
4

我通常将其用于可选参数。这和短路一样吗?

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

这使我可以选择传入 -1 或其他任何内容来说明对属性的可选检查。有时这涉及加入多个表,或者最好是一个视图。

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

于 2009-04-28T20:22:38.203 回答
4

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

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

但是,显然记录了一个 CASE 以按书面顺序评估 - 检查该博客文章的评论。

于 2012-08-14T19:33:27.387 回答
2

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

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

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

于 2009-04-25T16:32:26.643 回答
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# 示例将导致异常 ifsomeString == null因为它将被完全评估。在短路评估中,它每次都会起作用。

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

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

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

于 2009-04-25T18:25:23.080 回答
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

于 2009-04-25T22:58:04.267 回答
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

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

希望这对大家有帮助。

于 2012-07-25T14:27:42.733 回答
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 条件的左侧时,慢速函数会执行更多次。

于 2016-11-15T15:49:37.163 回答
0

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

SET @ADate = NULL

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

有保证的方式会很好!

于 2009-05-25T04:58:17.847 回答
-1

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

了解 T-SQL 表达式短路

于 2021-04-14T23:41:07.583 回答
-4

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

支持示例:

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,则将所有条件休息,直到 conditionN 被跳过。概括地说,在确定第一个TRUE时,将跳过由 OR 链接的所有其他条件。

考虑第二个条件

WHERE 1 = 0 AND 1 = 1

由于第一个条件被评估为FALSE,因此评估第二个条件毫无意义,因为它的任何值的评估都不会影响结果,因此 Sql Server 再次通过跳过不必要的条件检查或评估来节省查询执行时间的好机会.

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

condition1 AND condition2 AND ..... conditionN

如果 condition1 被评估为FALSE,则其余所有条件,直到conditionN被跳过。概括地说,在确定第一个FALSE时,将跳过由AND链接的所有其他条件。

因此,明智的程序员应该始终以这样一种方式对条件链进行编程,以便首先评估成本较低或最能消除的条件,或者以能够最大限度地利用短路的方式安排条件

于 2015-06-12T07:57:32.887 回答