# 为什么10 ^ 37/1会引发算术溢出错误？

11

``````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.``````

18

# 结果类型

``````SELECT
SQL_VARIANT_PROPERTY(1, 'BaseType')   AS [BaseType]  -- int
, SQL_VARIANT_PROPERTY(1, 'Precision')  AS [Precision] -- 10
, SQL_VARIANT_PROPERTY(1, 'Scale')      AS [Scale]     -- 0
;``````

``````Operation                              Result precision                       Result scale *
-------------------------------------------------------------------------------------------------
e1 + e2                                max(s1, s2) + max(p1-s1, p2-s2) + 1    max(s1, s2)
e1 - e2                                max(s1, s2) + max(p1-s1, p2-s2) + 1    max(s1, s2)
e1 * e2                                p1 + p2 + 1                            s1 + s2
e1 / e2                                p1 - s1 + s2 + max(6, s1 + p2 + 1)     max(6, s1 + p2 + 1)
e1 { UNION | EXCEPT | INTERSECT } e2   max(s1, s2) + max(p1-s1, p2-s2)        max(s1, s2)
e1 % e2                                min(p1-s1, p2 -s2) + max( s1,s2 )      max(s1, s2)

* The result precision and scale have an absolute maximum of 38. When a result
precision is greater than 38, the corresponding scale is reduced to prevent the
integral part of a result from being truncated.``````

``````e1: @big_number, a DECIMAL(38, 0)
-> p1: 38
-> s1: 0

e2: 1, an INT
-> p2: 10
-> s2: 0

e1 / e2
-> Result precision: p1 - s1 + s2 + max(6, s1 + p2 + 1) = 38 + max(6, 11) = 49
-> Result scale:                    max(6, s1 + p2 + 1) =      max(6, 11) = 11``````

``````e1 / e2
-> Result precision: 49 -> reduced to 38
-> Result scale:     11 -> reduced to 6

Note that 6 is the minimum possible scale it can be reduced to.
It may be between 6 and 11 inclusive.``````

# 这如何解释算术溢出

``````DECLARE @big_number    DECIMAL(38,0) = '1' + REPLICATE(0, 37);
DECLARE @one_million   INT           = '1' + REPLICATE(0, 6);

PRINT @big_number / @one_million;``````

``10000000000000000000000000000000.000000``

``````DECLARE @big_number   DECIMAL(38,0) = '1' + REPLICATE(0, 37);
DECLARE @one_million  INT           = '1' + REPLICATE(0, 6);

SELECT
SQL_VARIANT_PROPERTY(@big_number / @one_million, 'BaseType')  AS [BaseType]  -- decimal
, SQL_VARIANT_PROPERTY(@big_number / @one_million, 'Precision') AS [Precision] -- 38
, SQL_VARIANT_PROPERTY(@big_number / @one_million, 'Scale')     AS [Scale]     -- 6
;``````

# 危险的解决方法

``````DECLARE @big_number     DECIMAL(38,0)  = '1' + REPLICATE(0, 37);
DECLARE @big_number_f   FLOAT          = CAST(@big_number AS FLOAT);

SELECT
@big_number                           AS big_number      -- 10^37
, @big_number_f                         AS big_number_f    -- 10^37
, CAST(@big_number_f AS DECIMAL(38, 0)) AS big_number_f_d  -- 9999999999999999.5 * 10^21
;``````

2
RE：“更清洁的方式”。您可能想看看`SQL_VARIANT_PROPERTY`
Martin Smith

@Martin-您能否提供一个示例或快速说明，`SQL_VARIANT_PROPERTY`以说明如何使用像问题中讨论的那样进行除法？
Nick Chammas 2012年

1

Martin Smith

@Martin-啊，是的，整洁了！
Nick Chammas 2012年