继续我最近玩大数字的趋势,我最近将我遇到的一个错误归结为以下代码:
DECLARE @big_number DECIMAL(38,0) = '1' + REPLICATE(0, 37);
PRINT @big_number + 1;
PRINT @big_number - 1;
PRINT @big_number * 1;
PRINT @big_number / 1;
我得到的这段代码的输出是:
10000000000000000000000000000000000001
9999999999999999999999999999999999999
10000000000000000000000000000000000000
Msg 8115, Level 16, State 2, Line 6
Arithmetic overflow error converting expression to data type numeric.
什么?
为什么前 3 个操作有效但最后一个无效?如果@big_number
明明可以存储 的输出,怎么会出现算术溢出错误@big_number / 1
呢?
在算术运算的上下文中理解精度和比例
让我们分解一下,仔细看看除法算术运算符的细节。这是 MSDN 对除法运算符的结果类型的描述:
我们知道那
@big_number
是一个DECIMAL
. SQL Server 转换1
成什么数据类型?它将它转换为INT
. 我们可以通过以下方式确认这一点SQL_VARIANT_PROPERTY()
:对于踢球,我们还可以
1
将原始代码块中的 替换为显式键入的值,DECLARE @one INT = 1;
并确认我们得到相同的结果。所以我们有一个
DECIMAL
和一个INT
。由于DECIMAL
具有比更高的数据类型优先级INT
,我们知道除法的输出将被转换为DECIMAL
。那么问题出在哪里呢?
问题在于输出中的比例
DECIMAL
。以下是有关 SQL Server 如何确定算术运算结果的精度和小数位数的规则表:下面是我们在这个表中的变量:
根据上表中的星号注释, a可以具有的最大精度
DECIMAL
为 38 。所以我们的结果精度从 49 降低到 38,并且“相应的小数位数减少以防止结果的整数部分被截断”。从这个评论中不清楚比例是如何减少的,但我们知道这一点:根据表中的公式,将两个 s 相除后的最小可能小数位数
DECIMAL
是 6。因此,我们最终得到以下结果:
这如何解释算术溢出
现在答案很明显了:
我们部门的输出被转换为
DECIMAL(38, 6)
,DECIMAL(38, 6)
不能容纳 10 37。有了这个,我们可以通过确保结果适合来构建另一个成功的部门
DECIMAL(38, 6)
:结果是:
注意小数点后的 6 个零。我们可以确认结果的数据类型是
DECIMAL(38, 6)
通过使用SQL_VARIANT_PROPERTY()
如上:一个危险的解决方法
那么我们如何绕过这个限制呢?
好吧,这当然取决于您进行这些计算的目的。您可能会立即跳转到的一种解决方案是将您的数字转换为
FLOAT
用于计算,然后DECIMAL
在完成后将它们转换回。这在某些情况下可能有效,但您应该仔细了解这些情况是什么。众所周知,将数字相互转换
FLOAT
是危险的,可能会给您带来意想不到的或不正确的结果。在我们的例子中,将 10 37与10 37 相互转换
FLOAT
得到的结果完全错误:你有它。小心分开,我的孩子们。