为什么 SQL 服务器更改操作顺序并按其方式装箱?
Why is SQL Server changing operation order and boxing the way it does?
四个简单的SELECT语句:
SELECT 33883.50 * -1;
SELECT 33883.50 / -1.05;
SELECT 33883.50 * -1 / 1.05;
SELECT (33883.50 * -1) / 1.05;
但结果并不如我所料:
-33883.50
-32270.000000
-32269.96773000
-32270.000000
第三个结果似乎有问题。我可以看到发生了什么,首先 SQL 服务器评估这个:
SELECT -1 / 1.05;
得到答案:
-0.952380
然后它采用该答案并使用它来执行此计算:
SELECT 33883.50 * -0.952380;
得到以下的(错误)答案:
-32269.96773000
但它为什么要这样做?
你知道BODMAS规则吗。答案是正确的,不是因为 Sql 服务器,而是基础数学。
先是Division
然后是Subtraction
,所以除法总是先于减法
如果您想得到正确答案,请使用正确的括号
SELECT (33883.50 * -1) / 1.05;
T-SQL 有一个它遵循的运算符优先级规则。您可以在 link https://msdn.microsoft.com/en-us/library/ms190276.aspx.
上阅读相关信息
这似乎是关于一元运算符的优先规则。我尝试了以下查询
SELECT 33883.50 * cast(-1 as int) / 1.05;
SELECT 33883.50 * (-1 * 1) / 1.05;
它 returns 正确答案。最好的办法是在要首先出现的表达式上使用括号,然后彻底测试。
在你的例子中
33883.50 * -1 / 1.05
被评估为
33883.50 * (-1 / 1.05)
而不是
(33883.50 * -1) / 1.05
这会导致精度下降。
我玩了一下。我使用 SQL Sentry Plan Explorer 查看 SQL 服务器如何计算表达式的详细信息。例如,
2 * 3 * -4 * 5 * 6
被评估为
((2)*(3)) * ( -((4)*(5))*(6))
我会这样解释。在 T-SQL 中,一元减号变为 same priority as subtraction,低于乘法。是的,
When two operators in an expression have the same operator precedence
level, they are evaluated left to right based on their position in the
expression.
,但这里我们有一个表达式,它混合了具有不同优先级的运算符,并且解析器严格遵循这些优先级。乘法必须先进行,因此它首先计算 4 * 5 * 6
,然后对结果应用一元减法。
通常 (say in C++) 一元减号具有更高的优先级(如按位 NOT),这样的表达式会按预期进行解析和求值。他们应该使一元 minus/plus 与 T-SQL 中的按位非相同的最高优先级,但他们没有,这就是结果。所以,这不是错误,而是错误的设计决策。它甚至被记录在案,尽管相当模糊。
当您提到 Oracle 时 - 同样的示例在 Oracle 中的工作方式与在 SQL 服务器中的工作方式不同:
- Oracle 的 rules for operator precedence 可能与 SQL 服务器不同。所要做的就是使一元减去最高优先级。
- Oracle 在计算
decimal
类型的表达式时可能有不同的 rules for determining result precision and scale。
- Oracle 可能对舍入中间结果有不同的规则。 SQL Server "uses rounding when converting a number to a decimal or numeric value with a lower precision and scale".
- Oracle 可能对这些表达式使用完全不同的类型,而不是
decimal
。在SQL Server"a constant with a decimal point is automatically converted into a numeric data value, using the minimum precision and scale necessary. For example, the constant 12.345 is converted into a numeric value with a precision of 5 and a scale of 3."
- 甚至
decimal
的定义在 Oracle 中也可能不同。即使在 SQL Server "the default maximum precision of numeric and decimal data types is 38. In earlier versions of SQL Server, the default maximum is 28."
四个简单的SELECT语句:
SELECT 33883.50 * -1;
SELECT 33883.50 / -1.05;
SELECT 33883.50 * -1 / 1.05;
SELECT (33883.50 * -1) / 1.05;
但结果并不如我所料:
-33883.50
-32270.000000
-32269.96773000
-32270.000000
第三个结果似乎有问题。我可以看到发生了什么,首先 SQL 服务器评估这个:
SELECT -1 / 1.05;
得到答案:
-0.952380
然后它采用该答案并使用它来执行此计算:
SELECT 33883.50 * -0.952380;
得到以下的(错误)答案:
-32269.96773000
但它为什么要这样做?
你知道BODMAS规则吗。答案是正确的,不是因为 Sql 服务器,而是基础数学。
先是Division
然后是Subtraction
,所以除法总是先于减法
如果您想得到正确答案,请使用正确的括号
SELECT (33883.50 * -1) / 1.05;
T-SQL 有一个它遵循的运算符优先级规则。您可以在 link https://msdn.microsoft.com/en-us/library/ms190276.aspx.
上阅读相关信息这似乎是关于一元运算符的优先规则。我尝试了以下查询
SELECT 33883.50 * cast(-1 as int) / 1.05;
SELECT 33883.50 * (-1 * 1) / 1.05;
它 returns 正确答案。最好的办法是在要首先出现的表达式上使用括号,然后彻底测试。
在你的例子中
33883.50 * -1 / 1.05
被评估为
33883.50 * (-1 / 1.05)
而不是
(33883.50 * -1) / 1.05
这会导致精度下降。
我玩了一下。我使用 SQL Sentry Plan Explorer 查看 SQL 服务器如何计算表达式的详细信息。例如,
2 * 3 * -4 * 5 * 6
被评估为
((2)*(3)) * ( -((4)*(5))*(6))
我会这样解释。在 T-SQL 中,一元减号变为 same priority as subtraction,低于乘法。是的,
When two operators in an expression have the same operator precedence level, they are evaluated left to right based on their position in the expression.
,但这里我们有一个表达式,它混合了具有不同优先级的运算符,并且解析器严格遵循这些优先级。乘法必须先进行,因此它首先计算 4 * 5 * 6
,然后对结果应用一元减法。
通常 (say in C++) 一元减号具有更高的优先级(如按位 NOT),这样的表达式会按预期进行解析和求值。他们应该使一元 minus/plus 与 T-SQL 中的按位非相同的最高优先级,但他们没有,这就是结果。所以,这不是错误,而是错误的设计决策。它甚至被记录在案,尽管相当模糊。
当您提到 Oracle 时 - 同样的示例在 Oracle 中的工作方式与在 SQL 服务器中的工作方式不同:
- Oracle 的 rules for operator precedence 可能与 SQL 服务器不同。所要做的就是使一元减去最高优先级。
- Oracle 在计算
decimal
类型的表达式时可能有不同的 rules for determining result precision and scale。 - Oracle 可能对舍入中间结果有不同的规则。 SQL Server "uses rounding when converting a number to a decimal or numeric value with a lower precision and scale".
- Oracle 可能对这些表达式使用完全不同的类型,而不是
decimal
。在SQL Server"a constant with a decimal point is automatically converted into a numeric data value, using the minimum precision and scale necessary. For example, the constant 12.345 is converted into a numeric value with a precision of 5 and a scale of 3." - 甚至
decimal
的定义在 Oracle 中也可能不同。即使在 SQL Server "the default maximum precision of numeric and decimal data types is 38. In earlier versions of SQL Server, the default maximum is 28."