什么在 DECIMAL 列中四舍五入我的值?

What is rounding my values in DECIMAL columns?

我有一个 SQL 更新请求。我希望它只修改更新模型中为其提供值的列,因此我使用一般形式 myCol = ISNULL(@myParam, myCol)。这是完整的 SQL...

update Justif set 
DateTransaction = ISNULL(@dateTransaction,DateTransaction),
Cif = ISNULL(@cif,Cif),
NomFournisseur = ISNULL(@nomFournisseur,NomFournisseur),
MontantHT = ISNULL(@montantHT,MontantHT),
MontantTtc = ISNULL(@montantTtc,MontantTtc),
TauxTva = ISNULL(@tauxTva,TauxTva),
MontantTva = ISNULL(@montantTva,MontantTva),
ReceptionNumber = ISNULL(@receptionNumber,ReceptionNumber),
Locked = IIF(@locked > 0,GETDATE(),null),
Used = IIF(@used is not null, @used, Used),
NatureOcr = ISNULL(@natureOcr, NatureOcr)
where JustifID = @justifId

现在,最奇怪的是,应用程序一度使用此请求来设置列 Used

montantTtc 参数与所有其他参数一样,用 DBNull.Value 初始化(并且 SqlDbType 设置为十进制),然后令我非常惊讶的是,小数列四舍五入到最接近的 int.

我对 ISNULL() 有什么不理解的地方?

参数类型推断再次来袭。 Decimal SQL 参数 NULL 作为 DECIMAL(29,0) 传递。 ISNULL returns 第一个参数的类型,由它负责其余部分。 reproduce/prove 的简短代码片段:

using (var connection = new SqlConnection(@"Data Source=(localdb)\MSSQLLocalDB")) {
    connection.Open();
    using (var command = new SqlCommand(@"
        DECLARE @v DECIMAL(4,1) = 123.4; 
        SELECT 
            ISNULL(@p, @v),
            SQL_VARIANT_PROPERTY(ISNULL(@p, @v), 'Precision'), 
            SQL_VARIANT_PROPERTY(ISNULL(@p, @v), 'Scale')"
    )) {
        command.Connection = connection;
        command.Parameters.Add(new SqlParameter("@p", SqlDbType.Decimal)).Value = DBNull.Value;
        using (var reader = command.ExecuteReader()) {
            reader.Read();
            Console.WriteLine($@"
                Munged value: {reader.GetValue(0)}
                Precision: {reader.GetValue(1)}
                Scale: {reader.GetValue(2)}
            ");
        }
    }
}

Munged value: 123
Precision: 29
Scale: 0

正确修复是根据列提供参数的PrecisionScale。另一种方法是使用

COALESCE(@p, @v)

相当于

形式的表达式
CASE WHEN @p IS NOT NULL THEN @p ELSE @v END

两者都将应用 DECIMAL 升级规则(这会导致 DECIMAL(30,1))。请注意,如果源类型具有很高的精度,这 可能不安全 :使用 DECLARE @v DECIMAL(17,10) = 123.0123456789 将得到 123.012345679 的舍入 DECIMAL(38,9)。唯一真正通用的修复方法是使用列的确切类型。