精度、小数位数和长度 (Transact-SQL)
运算 结果精度 结果小数位数
--------- --------------- ------------
e1 * e2 p1 + p2 + 1 s1 + s2
* 结果精度和小数位数的绝对最大值为 38。 当结果精度大于 38 时,相应的小数位数会减少,以避免结果的整数部分被截断。
MSDN只是笼统地说截小数。而在参与帖子sql小数相乘出现零值问题 讨论时测试发现,整数也是可以截断的,但是并不是按整数的有效位数进行处理,而是有其它的规律。
A)小数位足够截,直接截小数。
条件:(s1+s2) > (p1+p2+1-38)
结果:p3 = 38, s3 = (s1+s2) - (p1+p2+1-38)
B)小数位不够截并且小数位不到6,保留全部小数位。
条件:((s1+s2) <= (p1+p2+1-38)) AND ((s1+s2) < 6)
结果:p3 = 38, s3 = s1+s2
C)小数位不够截并且小数位超过6,固定保留6个小数位。
条件:((s1+s2) <= (p1+p2+1-38)) AND ((s1+s2) >= 6)
结果:p3 = 38, s3 = 6
按照wuyongjun198提供的例子,重新归纳。处理优先级调整成B、C、A——2015年5月29日
条件:(s1+s2) < 6
结果:p3 = 38, s3 = s1+s2
条件:(s1+s2) - (p1+p2+1-38) < 6
结果:p3 = 38, s3 = 6
条件:(s1+s2) - (p1+p2+1-38) >> 6
结果:p3 = 38, s3 = (s1+s2) - (p1+p2+1-38)
SELECT SQL_VARIANT_PROPERTY(v,'BaseType') BaseType,
SQL_VARIANT_PROPERTY(v,'Precision') [Precision],
SQL_VARIANT_PROPERTY(v,'Scale') Scale,
v
FROM (
-- 方式A
SELECT CONVERT(sql_variant, cast(1.0 as decimal(38,23)) * cast(1.0 as decimal(38,23)) ) AS v
UNION ALL
SELECT CONVERT(sql_variant, cast(1.0 as decimal(28,23)) * cast(1.0 as decimal(28,23)) ) AS v
UNION ALL
SELECT CONVERT(sql_variant, cast(1.0 as decimal(28,15)) * cast(1.0 as decimal(28,15)) ) AS v
UNION ALL
SELECT CONVERT(sql_variant, cast(1.0 as decimal(28,16)) * cast(1.0 as decimal(28,16)) ) AS v
-- 方式B
UNION ALL
SELECT CONVERT(sql_variant, cast(1.0 as decimal(38,0)) * cast(1.0 as decimal(38,0)) ) AS v
UNION ALL
SELECT CONVERT(sql_variant, cast(1.0 as decimal(38,0)) * cast(1.0 as decimal(38,1)) ) AS v
UNION ALL
SELECT CONVERT(sql_variant, cast(1.0 as decimal(38,1)) * cast(1.0 as decimal(38,1)) ) AS v
UNION ALL
SELECT CONVERT(sql_variant, cast(1.0 as decimal(38,1)) * cast(1.0 as decimal(38,2)) ) AS v
UNION ALL
SELECT CONVERT(sql_variant, cast(1.0 as decimal(38,1)) * cast(1.0 as decimal(38,3)) ) AS v
UNION ALL
SELECT CONVERT(sql_variant, cast(1.0 as decimal(38,1)) * cast(1.0 as decimal(38,4)) ) AS v
-- 方式C
UNION ALL
SELECT CONVERT(sql_variant, cast(1.0 as decimal(38,1)) * cast(1.0 as decimal(38,5)) ) AS v
UNION ALL
SELECT CONVERT(sql_variant, cast(1.0 as decimal(38,1)) * cast(1.0 as decimal(38,6)) ) AS v
UNION ALL
SELECT CONVERT(sql_variant, cast(1.0 as decimal(28,3)) * cast(1.0 as decimal(28,6)) ) AS v
-- 方式C补充
UNION ALL
SELECT CONVERT(sql_variant, cast(1.0 as decimal(38,20)) * cast(1.0 as decimal(38,19)) ) AS v
UNION ALL
SELECT CONVERT(sql_variant, cast(1.0 as decimal(38,20)) * cast(1.0 as decimal(38,20)) ) AS v
UNION ALL
SELECT CONVERT(sql_variant, cast(1.0 as decimal(38,20)) * cast(1.0 as decimal(38,21)) ) AS v
UNION ALL
SELECT CONVERT(sql_variant, cast(1.0 as decimal(38,20)) * cast(1.0 as decimal(38,22)) ) AS v
UNION ALL
SELECT CONVERT(sql_variant, cast(1.0 as decimal(38,20)) * cast(1.0 as decimal(38,23)) ) AS v
UNION ALL
SELECT CONVERT(sql_variant, cast(1.0 as decimal(38,20)) * cast(1.0 as decimal(38,24)) ) AS v
-- 方式A补充
UNION ALL
SELECT CONVERT(sql_variant, cast(1.0 as decimal(38,20)) * cast(1.0 as decimal(38,25)) ) AS v
UNION ALL
SELECT CONVERT(sql_variant, cast(1.0 as decimal(38,20)) * cast(1.0 as decimal(38,26)) ) AS v
UNION ALL
SELECT CONVERT(sql_variant, cast(1.0 as decimal(38,20)) * cast(1.0 as decimal(38,27)) ) AS v
) T
BaseType Precision Scale v
----------- ----------- ------- ---------------------------------------
decimal 38 7 1.0000000
decimal 38 27 1.000000000000000000000000000
decimal 38 11 1.00000000000
decimal 38 13 1.0000000000000
BaseType Precision Scale v
----------- ----------- ------- ---------------------------------------
decimal 38 0 1
decimal 38 1 1.0
decimal 38 2 1.00
decimal 38 3 1.000
decimal 38 4 1.0000
decimal 38 5 1.00000
BaseType Precision Scale v
----------- ----------- ------- ---------------------------------------
decimal 38 6 1.000000
decimal 38 6 1.000000
decimal 38 6 1.000000
BaseType Precision Scale v
----------- ----------- ------- ---------------------------------------
decimal 38 6 1.000000
decimal 38 6 1.000000
decimal 38 6 1.000000
decimal 38 6 1.000000
decimal 38 6 1.000000
decimal 38 6 1.000000
BaseType Precision Scale v
----------- ----------- ------- ---------------------------------------
decimal 38 6 1.000000
decimal 38 7 1.0000000
decimal 38 8 1.00000000
有意思的是,当不需要截断时(p1+p2+1<=38),总有效位数只能利用到37。
而进行截断时,38个有效位都能利用起来。
SELECT SQL_VARIANT_PROPERTY(v,'BaseType') BaseType,
SQL_VARIANT_PROPERTY(v,'Precision') [Precision],
SQL_VARIANT_PROPERTY(v,'Scale') Scale,
v
FROM (
SELECT CONVERT(sql_variant, cast(POWER(10.0,18) as decimal(20,0)) * cast(POWER(10.0,19) as decimal(20,0)) ) AS v
UNION ALL
SELECT CONVERT(sql_variant, cast(POWER(10.0,17) as decimal(38,1)) * cast(POWER(10.0,18) as decimal(38,1)) ) AS v
UNION ALL
SELECT CONVERT(sql_variant, cast(POWER(10.0,15) as decimal(38,6)) * cast(POWER(10.0,16) as decimal(38,6)) ) AS v
) T
BaseType Precision Scale v
----------- ----------- ------- ---------------------------------------
decimal 38 0 10000000000000000000000000000000000000
decimal 38 2 100000000000000000000000000000000000.00
decimal 38 6 10000000000000000000000000000000.000000
只要对整数有截断(方式B、C),就存在溢出的可能。
下面的语句都会出溢出错误。
SELECT CONVERT(sql_variant, cast(POWER(10.0,19) as decimal(20,0)) * cast(POWER(10.0,19) as decimal(20,0)) ) AS v
SELECT CONVERT(sql_variant, cast(POWER(10.0,18) as decimal(38,1)) * cast(POWER(10.0,18) as decimal(38,1)) ) AS v
SELECT CONVERT(sql_variant, cast(POWER(10.0,16) as decimal(38,6)) * cast(POWER(10.0,16) as decimal(38,6)) ) AS v