我有一个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设置为小数),然后令我惊讶的是,小数列四舍五入为最接近的{{1 }}。
我对int
不了解?
答案 0 :(得分:2)
再次触发参数类型推断。 Decimal
的{{1}} SQL参数作为NULL
传递。 DECIMAL(29,0)
返回其第一个参数的类型,其余参数将由该参数负责。简短的代码片段可以重现/证明这一点:
ISNULL
Munged值:123
精度:29
比例尺:0
正确的解决方案是根据该列提供参数的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)}
");
}
}
}
和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
的四舍五入DECIMAL(38,9)
。唯一真正通用的解决方法是使用列的确切类型。