什么在 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
正确修复是根据列提供参数的Precision
和Scale
。另一种方法是使用
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)
。唯一真正通用的修复方法是使用列的确切类型。
我有一个 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
正确修复是根据列提供参数的Precision
和Scale
。另一种方法是使用
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)
。唯一真正通用的修复方法是使用列的确切类型。