当我从 VARCHAR(MAX) 转换为 Numeric(P,S) 时,为什么 SQL 服务器会覆盖我的比例值?

Why does SQL Server override my scale value when I am casting from VARCHAR(MAX) to Numeric(P,S)?

我想了解为什么 SQL 服务器在我将值从 VARCHAR(MAX) 转换为 NUMERIC(P,S) 时修改我的标度值。

示例如下:

--declare @val1 as numeric (18,6)
--declare @val2 as numeric (18,6)

DECLARE @val1 AS VARCHAR(MAX)
DECLARE @val2 AS VARCHAR(MAX)

SET @val1 = '2383.4912500000'
SET @val2 = '2383.4912000000'

DROP TABLE IF EXISTS TMP

SELECT
    V1 = @VAL1, 
    VC1 = CAST(@VAL1 AS NUMERIC(30, 20)),
    valDiff = ABS(CAST(CAST(@VAL2 AS NUMERIC(18, 6)) - CAST(@VAL1 AS NUMERIC(18, 6)) AS NUMERIC(30, 20)) / @VAL1) 
INTO  
    TMP

SELECT * FROM TMP

-- when variable is VARCHAR(MAX), valDiff == 0.00000002 which is correct, but rounded.

-- when the variable is NUMERIC(18, 6), valDiff == 0.0000000209776310275945 which is correct and not rounded

当您从精度非常高的小数开始时,算术运算的结果可能会缩小其小数位数以保留更多有效数字。如果值 没有 30 non-zero 小数点左边的数字并不重要。转换基于声明的精度和小数位数:

Precision and Scale

这里您的 varchar 正在以过高的精度隐式转换为小数。

由于需要小心地将中间结果转换为小数,使用 float 执行所有计算通常更容易,它在所有尺度上保持约 15 位有效数字的精度,并且只转换最终结果到 decimal 进行存储。例如

declare @val1 as float ='2383.4912500000'
declare @val2 as float ='2383.4912000000'

select cast( abs(@VAL2 - @VAL1) / @VAL1 as numeric(30,20) )