看似不正确的运营商优先权

Eli*_*Eli 7 sql-server

我在某些运算符优先级问题上遇到了一些困难,我希望下面的所有查询都失败; 这是基于下面的文档,其中将在比较运算符之前评估除法.

用于t-sql运算符优先级的Microsoft文档:https://docs.microsoft.com/en-us/sql/t-sql/language-elements/operator-precedence-transact-sql? view = sql-server-2017

示例数据和查询

CREATE TABLE #orders(
    price INT
)
GO 

INSERT INTO #orders VALUES 
(1)
,(2)
,(3)
,(4)
,(5)
,(6)
,(7)
,(8)
,(9)
,(10)
,(0)  

GO

/*
the following two queries work
*/
SELECT * 
FROM   #orders 
WHERE  2 / price > 0 
       AND price > 0; 

SELECT * 
FROM   #orders 
WHERE  price > 0 
       AND 2 / price > 0; 

/*
these don't work - getting divide by zero error
*/
SELECT * 
FROM   #orders 
WHERE  2 / price > 0 
       AND price IN ( 1, 2, 3, 4, 
                      5, 6, 7, 8, 
                      9, 10 ); 

SELECT * 
FROM   #orders 
WHERE  price IN ( 1, 2, 3, 4, 
                  5, 6, 7, 8, 
                  9, 10 ) 
       AND 2 / price > 0; 
Run Code Online (Sandbox Code Playgroud)

其中一个有效查询的共享执行计划:https://www.brentozar.com/pastetheplan/?id = HJFH3JWB7

其中一个非工作查询的共享(估计)执行计划:https://www.brentozar.com/pastetheplan/?id = r1Ds2yWSQ

任何帮助理解这种行为将不胜感激!

谢谢,

礼

Joe*_*ell 3

好问题。我认为您共享的文档页面上的语言可能有点不精确,这可能会导致您感到困惑。这是引用(强调我的):

\n\n
\n

当复杂表达式有多个运算符时,运算符优先级决定了运算的执行顺序。执行顺序可能 会显着影响结果值。

\n
\n\n

这里强烈的暗示是,优先级和执行顺序本质上是同一件事,但事实并非如此。埃里克·利珀特(Eric Lippert)对此的解释比我更好(原文中的重点)。

\n\n
\n

优先级规则描述当表达式混合不同类型的运算符时,应如何为带下括号的表达式添加括号。例如,乘法的优先级高于加法,因此 2 + 3 x 4 相当于 2 + (3 x 4),而不是 (2 + 3) x 4。

\n\n

结合性规则描述了当表达式有一堆相同类型的运算符时,下括号表达式应如何加上括号。例如,加法是从左到右结合的,因此 a + b + c 等价于 (a + b) + c,而不是 a + (b + c)。在普通算术中,这两个表达式总是给出相同的结果;在计算机算术中,它们不一定。(作为练习,您能否找到 a、b、c 的值,使得 (a + b) + c 不等于 C# 中的 a + (b + c)?)

\n\n

现在是令人困惑的一个。

\n\n

评估顺序描述了表达式中每个操作数的求值顺序。括号只是描述结果如何组合在一起;\xe2\x80\x9cdo 括号在前\xe2\x80\x9d 不是 C# 的规则\n。相反,C# 中的规则是 \xe2\x80\x9c 从左到右\xe2\x80\x9d 严格评估每个子表达式。

\n
\n\n

阅读全文。Eric 使用 C# 和 C++ 作为示例,但他的评论比这更普遍。正如他所建议的,如果您将运算符优先级规则视为控制表达式组件的分组方式而不是它们的执行顺序,那么您所看到的行为就更有意义。这是您期望失败但实际上成功的查询之一的简化​​版本:

\n\n
declare @t table (x int);\ninsert @t values (0);\n\nselect * from @t where 2 / x > 0 and x > 0;\n
Run Code Online (Sandbox Code Playgroud)\n\n

SQL Server 的运算符优先级规则只是意味着上面的查询等效于:

\n\n
-- Same query as above with operator precedence shown explicitly.\nselect * from @t where ((2 / x) > 0) and (x > 0);\n
Run Code Online (Sandbox Code Playgroud)\n\n

在这种情况下,有趣的AND是,如果您知道其中一个操作数为假,那么您甚至无需评估另一个操作数就知道整个表达式为假。某些编程语言指定操作数求值发生的顺序。例如,C# 中的 && 运算符首先计算其左侧操作数,如果为 false,则根本不计算其右侧操作数。但是ANDT-SQL 中的运算符不会以任何一种方式做出声明;由优化器来选择。

\n\n

有很多文章致力于详细探讨这种行为;例如,这是一个非常详细的。这有帮助吗?

\n