SQL Server中的一个奇怪的操作问题:-100 / -100 * 10 = 0

105
  • 如果执行 SELECT -100/-100*10,结果为 0
  • 如果执行 SELECT (-100/-100)*10,结果为 10
  • 如果执行 SELECT -100/(-100*10),结果为 0
  • 如果执行 SELECT 100/100*10,结果为 10

BOL 表示:

当表达式中两个操作符具有相同的优先级时,它们将根据它们在表达式中的位置从左到右进行评估。

然后

Level   Operators
  1     ~ (Bitwise NOT)
  2     * (Multiplication), / (Division), % (Modulus)
  3     + (Positive), - (Negative), + (Addition), + (Concatenation), - (Subtraction), & (Bitwise AND), ^ (Bitwise Exclusive OR), | (Bitwise OR)

BOL错了,还是我漏掉了什么?似乎-使得(预期的)优先级发生了变化。


7
你有什么问题? - Ilyes
14
为什么你认为与比特有关,你正在使用整数。而整数/整数=整数。所以-100/-1000等于0。 - sepupic
5
好的,我同意,似乎 - 导致了计算结果出现了问题。如果你尝试计算 -100/(-100)*10,会得到结果 10。看起来 / 运算符被应用于方程中的负数 - 值上,然后计算了 100*10 的值。我不确定这是否是 BOL 的错误,更可能是 SQL Server 的行为与预期不同。可以考虑在 sql-docs 上提出问题,看看他们的回应;也许可以在文档中添加一条注释,提示这个“特性”。 - Thom A
3
SELECT -100/(-100)*10的结果是10。看起来-被当作负号运算符,应该在计算完100*10后才应用。 - Panagiotis Kanavos
7
"A / -B * C" 翻译为 "A <div> <negate> B <multiply> C"。根据文档,取反运算的优先级低于乘法,因此结果是 "A / -(B * C)"。通过使用浮点常量,可以更清楚地看到这一点: "12e / -13e * 14e" 与 "(12e / -13e) * 14e" 和 "12e / 13e * 14e"。这让我们感到困惑的原因是因为我们通常希望一元减号成为字面量的一部分,或者至少具有非常高的优先级,但这不是 T-SQL 的工作方式。 - Jeroen Mostert
显示剩余17条评论
3个回答

96
根据优先级表,这是预期的行为。具有更高优先级(/和*)的运算符在具有较低优先级(一元-)的运算符之前进行求值。因此,这样做:
-100 / -100 * 10

被评估为:

-(100 / -(100 * 10))

请注意,这种行为与大多数编程语言不同,其中一元否定具有比乘法和除法更高的优先级,例如VBJavaScript

38
哇,T-SQL又有一个宝石功能啦 :) 我猜现在我需要审计我的所有代码以寻找漏洞。 - usr
14
哦,天啊。这比各种PHP三元运算符错误还要糟糕。谁能想得到一元运算符的优先级必须低于二元运算符呢?请问这是哪些理智的人认为的? - phuclv
12
真正的差别可能在于是否将“-”视为“-100”中的运算符。在某些语言中,它是整数语法的一部分。 - Barmar
7
他们的一元负号优先级存在问题,这是一个bug。 - Kevin
4
反直觉设计大奖的获得者是...:再次当选的微软。 - rexkogitans
显示剩余8条评论

34

BOL是正确的。 - 的优先级低于*,因此

-A * B

被解析为

-(A * B)

由于乘法的特性,通常情况下你并不会注意到这一点,除非混合使用两个其他二元运算符具有相等优先级的运算符:/%(而且在像这样的复合表达式中很少使用%)。因此

C / -A * B

被解析为

C / -(A * B)

解释结果。这是反直觉的,因为在大多数其他语言中,一元减运算符比*/具有更高的优先级,但在T-SQL中不是如此,并且文档记录正确。

一个好(?)的说明:

SELECT -1073741824 * 2

产生算术溢出,因为-(1073741824 * 2)会产生2147483648作为中间值,它无法容纳在INT中,但是

SELECT (-1073741824) * 2

产生了预期的结果-2147483648,这样做。


"Minus" 是二进制的。一元运算符 - 是表示"负数"。那些说"minus 10"而实际上指的是"negative 10"的人是不准确的。 - Acccumulation
11
@Accumulation:这里的不精确性并非由我引起。在SQL查询计划中,当应用于单个操作数时,-运算符被称为“MINUS”。它的二元对应物称为“SUB”。如果您愿意,可以将“unary minus”解释为“由减号表示的一元运算符”的简写——这是一个语法上而非语义上的指示。请注意,本次翻译已经尽可能通俗易懂,无需进一步解释。 - Jeroen Mostert
3
“negative 10”是标准的美式用法(我相信),但在英国则不是标准用法。 - Alchymist

12

请注意文档中,- (负号) 的优先级是第三位(也许与直觉相反)。

因此,实际上你得到的结果是:

-(100/-(100*10)) = 0

如果将它们放入变量中,你就看不到发生这种情况,因为乘法后没有一元操作。

所以,在这里A和B是相同的,而C、D、E则显示了你看到的结果(其中E拥有完整的括号)。

DECLARE @i1 int, @i2 int, @i3 int;

SELECT @i1 = -100,
       @i2 = -100,
       @i3 = 10;

SELECT @i1/@i2*@i3      [A],
       -100/(-100)*10   [B],
       -100/-100*10     [C],
       -100/-(100*10)   [D],
       -(100/-(100*10)) [E];

A - 10
B - 10
C - 0
D - 0
E - 0

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