是否对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是否为NULL并且@key = t.Key是否已计算?
如果不是,为什么不呢?
如果是,是否有保证? 它是ANSI SQL的一部分还是特定于数据库?
如果是数据库特定的,SqlServer?甲骨文?MySQL的?
参考:短路评估
14个解决方案
66 votes
ANSI SQL Draft 2003 5WD-01-Framework-2003-09.pdf
6.3.3.3规则评估顺序
[...]
优先权不是由格式决定的 括号,表达式的有效评价一般 从左到右执行。 但是,确实如此 依赖于实现的表达式是否从左到右实际计算,特别是当操作数或运算符可能时 引起条件提升或表达结果如何 可以在不完全评估的所有部分的情况下确定 表达。
Andre Figueiredo answered 2019-06-06T21:26:40Z
55 votes
从上面来看,短路并不是真的可用。
如果您需要,我建议一个Case声明:
Where Case when Expr1 then Expr2 else Expr3 end = desiredResult
始终评估Expr1,但每行仅评估Expr2和Expr3中的一个。
PMc answered 2019-06-06T21:27:18Z
18 votes
我认为这是我写它的情况之一,好像它没有短路,原因有三个。
因为对于MSSQL来说,通过在显而易见的位置查看BOL无法解决问题,所以对我而言,这使得它在规范上变得模棱两可。
因为至少我知道我的代码会起作用。 更重要的是,那些追随我的人也是如此,所以我不会让他们一遍又一遍地担心同样的问题。
我经常写几个DBMS产品,如果我能轻松解决这些问题,我不想记住这些差异。
dkretz answered 2019-06-06T21:28:03Z
12 votes
我不相信SQL Server(2005)中的短路是有保障的。 SQL Server通过其优化算法运行您的查询,该算法考虑了很多事情(索引,统计信息,表大小,资源等)以提出有效的执行计划。 在此评估之后,您无法确定您的短路逻辑是否得到保证。
我前一段时间遇到了同样的问题,我的研究确实没有给出一个明确的答案。 你可以编写一个小的查询来给你一个证明它有效的证据,但是你可以确定随着数据库负载的增加,表变得更大,并且数据库中的事情得到优化和改变,结论将是 保持。 我不能因此而谨慎,并在WHERE子句中使用CASE来确保短路。
Mehmet Aras answered 2019-06-06T21:28:35Z
6 votes
您必须记住数据库的工作方式。 给定参数化查询,db基于该查询构建执行计划,而不使用参数值。 无论实际提供的值是什么,每次运行查询时都会使用此查询。 具有特定值的查询短路是否与执行计划无关。
Logicalmind answered 2019-06-06T21:29:00Z
3 votes
我通常将它用于可选参数。 这与短路相同吗?
SELECT [blah]
FROM Emp
WHERE ((@EmpID = -1) OR (@EmpID = EmpID))
这使我可以选择传入-1或其他任何因素来考虑属性的可选检查。 有时这涉及连接多个表,或者最好是视图。
非常方便,不完全确定它为db引擎提供的额外工作。
p.campbell answered 2019-06-06T21:29:37Z
2 votes
对于SQL Server,我认为这取决于版本,但我对SQL Server 2000的经验是,即使@key为null,它仍会评估@key = t.Key。 换句话说,在评估WHERE子句时,它不会进行有效的短路。
我见过人们推荐像你的例子这样的结构作为一种灵活查询的方式,用户可以输入或不输入各种标准。 我的观察是当@key为null时Key仍然参与查询计划,如果Key被索引,那么它不会有效地使用索引。
这种具有不同标准的灵活查询可能是一种情况,其中动态创建的SQL确实是最好的方法。 如果@key为null,那么根本不将它包含在查询中。
tetranz answered 2019-06-06T21:30:16Z
2 votes
我不知道短路,但我会把它写成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]
DForck42 answered 2019-06-06T21:30:54Z
2 votes
只是偶然发现了这个问题,并且已经找到了这个博客条目:[http://rusanu.com/2009/09/13/on-sql-server-boolean-operator-short-circuit/]
SQL服务器可以在她认为合适的任何地方自由地优化查询,因此在博客文章中给出的示例中,您不能依赖于短路。
但是,CASE显然需要记录以书面顺序进行评估 - 检查该博客文章的评论。
stolsvik answered 2019-06-06T21:31:32Z
1 votes
短路评估的主要特征是一旦确定结果就停止评估表达式。 这意味着可以忽略表达式的其余部分,因为无论是否进行求值,结果都是相同的。
二进制布尔运算符是可交换的,意思是:
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");
如果someString == null,这个C#示例将导致异常,因为它将被完全评估。 在短路评估中,它每次都会工作。
SQL只对未能初始化的标量变量(无对象)进行操作,因此无法编写无法计算的布尔表达式。 如果您有一些NULL值,则任何比较都将返回false。
这意味着在SQL中,您无法编写根据使用短路或完整评估而进行不同评估的表达式。
如果SQL实现使用短路评估,它只能加速查询执行。
zendar answered 2019-06-06T21:32:45Z
1 votes
下面是对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
这需要几秒钟,表明短路行为不再存在,并且正在评估每条记录的复杂操作。
希望这有助于你们。
Jorge answered 2019-06-06T21:33:43Z
1 votes
这是一个演示来证明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条件的左侧时,慢执行一次慢功能。
Steve Chambers answered 2019-06-06T21:34:42Z
0 votes
这在查询分析器中需要额外4秒,所以我可以看到IF甚至没有短路...
SET @ADate = NULL
IF (@ADate IS NOT NULL)
BEGIN
INSERT INTO #ABla VALUES (1)
(SELECT bla from a huge view)
END
有一个保证的方式会很好!
bluish answered 2019-06-06T21:35:13Z
-3 votes
很明显,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”连接的整个链将被视为评估为真,而不评估其他条件。
condition1 OR condition2 OR ..... OR conditionN
如果条件1被评估为真,则休息所有条件,直到条件N被跳过。在确定第一个TRUE时的广义词中,将跳过由OR链接的所有其他条件。
考虑第二个条件
WHERE 1 = 0 AND 1 = 1
因为第一个条件是被评估为FALSE,它无意义地评估第二个条件,因为它的评估无论什么价值根本不会影响结果,所以再次为Sql Server提供了通过跳过不必要的条件检查或评估来节省查询执行时间的好机会。
在“AND”的情况下,如果第一个条件被评估为FALSE,则与“AND”连接的整个链将被视为评估为FALSE而不评估其他条件。
condition1 AND condition2 AND ..... conditionN
如果condition1被评估为FALSE,则休息所有条件,直到条件N被跳过。在确定第一个FALSE时的广义词中,将跳过由AND链接的所有其他条件。
因此,一个聪明的程序员应该总是按照这样的方式编写条件链,这种方式很少,或者说消极的条件首先得到评估,或者以这样的方式安排条件,以获得短路的最大好处
RkHirpara answered 2019-06-06T21:37:27Z