SQL 服务器十进制计算和默认值

SQL Server decimal calculation and default value

谁能解释一下下面的结果。我读过SQL小数类型和乘法和除法时的精度和小数位数,但我仍然无法理解:

select cast(7 as decimal(25,13))*cast(15 as decimal(25,13)) = 105.0000000000000

select cast(15 as decimal(25,13))/cast(11 as decimal(25,13)) = 1.3636363636363

select cast(7 as decimal(25,13))*cast(15 as decimal(25,13))/cast(11 as decimal(25,13)) = 9.545454

select cast(cast(7 as decimal(25,13))*cast(15 as decimal(25,13)) as decimal(25,13))/cast(11 as decimal(25,13)) = 9.5454545454545

所以乘法和除法产生 13 位小数,但当链接时它们突然产生 6 位小数。只有当乘法首先转换为 decimal(25,13) 然后再除法时,它才会再次产生 13 位小数。 SQL 服务器是否将乘法转换为默认的小数 (38,0) 然后除以小数 (25,13)?

我正在为非常低价的产品进行 bom 计算,我想要我能得到的所有小数。我是否需要在每一步都进行这种强制转换,或者我是否可以以某种方式为单个查询设置默认值以在所有小数点上使用此 decimal(25,13)(如果没有另外说明)?

我尝试做乘法然后转换为十进制

select cast(7*15 as decimal(25,13))/cast(11 as decimal(25,13))

以下是 precision, scale, and length documentation 中十进制乘法和除法行为上下文的相关摘录。

+--------------------------------------+-------------------------------------+---------------------+
|              Operation               |          Result precision           |   Result scale *    |
+--------------------------------------+-------------------------------------+---------------------+
| e1 * e2                              | p1 + p2 + 1                         | s1 + s2             |
| e1 / e2                              | p1 - s1 + s2 + max(6, s1 + p2 + 1)  | max(6, s1 + p2 + 1) |
+--------------------------------------+-------------------------------------+---------------------+

* The result precision and scale have an absolute maximum of 38. When a result precision is greater than 38, it's reduced to 38, and the corresponding scale is reduced to try to prevent truncating the integral part of a result. In some cases such as multiplication or division, scale factor won't be reduced, to maintain decimal precision, although the overflow error can be raised.

In multiplication and division operations, we need precision - scale places to store the integral part of the result. The scale might be reduced using the following rules:

1. The resulting scale is reduced to min(scale, 38 - (precision-scale)) if the integral part is less than 32, because it can't be greater than 38 - (precision-scale). Result might be rounded in this case.

2. The scale won't be changed if it's less than 6 and if the integral part is greater than 32. In this case, overflow error might be raised if it can't fit into decimal(38, scale)

3. The scale will be set to 6 if it's greater than 6 and if the integral part is greater than 32. In this case, both integral part and scale would be reduced and resulting type is decimal(38,6). Result might be rounded to 6 decimal places or the overflow error will be thrown if the integral part can't fit into 32 digits.

项目 #3 适用于您问题中的第三个查询,可以通过 sp_describe_first_result_set:

观察到
EXEC sp_describe_first_result_set N'
select cast(7 as decimal(25,13))*cast(15 as decimal(25,13))/cast(11 as decimal(25,13)) --= 9.545454
';

上述查询中的 system_type_name 显示 decimal(38,6)。由于 6 的表达式结果比例不足以满足您的需要,因此乘法表达式的显式 CAST 为精度较低的小数类型(就像您在问题的最后一个查询中所做的那样)将为结果类型,但溢出的风险增加。

CASTdecimal(25, 13) 的显式结果类型为 decimal(38, 13):

EXEC sp_describe_first_result_set N'
select cast(
       cast(7 as decimal(25,13))*cast(15 as decimal(25,13)) as decimal(25,13))/cast(11 as decimal(25,13)) --= 9.5454545454545
';