У меня возникают неожиданные результаты при работе с SqlDecimals. В конце концов, кажется, что это сводится к проблемам масштабирования при делении 2 SqlDecimals.
Пример кода С# выглядит так:
[Microsoft.SqlServer.Server.SqlFunction]
[return: SqlFacet(Precision = 38, Scale = 8)]
public static SqlDecimal fn_divide([SqlFacet(Precision = 38, Scale = 8)]SqlDecimal x, [SqlFacet(Precision = 38, Scale = 8)]SqlDecimal y)
{
var r = SqlDecimal.Divide(@x, @y);
return r;
}
Код теста SQL выглядит следующим образом:
DECLARE @x numeric(38, 8),
@y numeric(38, 8)
SELECT @x = Replicate('1', 28) + '.12345678',
@y = '0.25896314'
SELECT via = 'TSQL', x = @x, y = @y, r = Convert(numeric(38, 8), @x / @y)
SELECT via = 'SQLCLR', x = @x, y = @y, r = dbo.fn_divide(@x, @y)
Второй выбор возвращает мне следующую ошибку:
A .NET Framework error occurred during execution of user-defined routine or aggregate "fn_divide": `
System.OverflowException: Arithmetic Overflow.
System.OverflowException:
at System.Data.SqlTypes.SqlDecimal.MultByULong(UInt32 uiMultiplier)
at System.Data.SqlTypes.SqlDecimal.AdjustScale(Int32 digits, Boolean fRound)
at System.Data.SqlTypes.SqlDecimal.op_Division(SqlDecimal x, SqlDecimal y)
at System.Data.SqlTypes.SqlDecimal.Divide(SqlDecimal x, SqlDecimal y)
at UserDefinedFunctions.fn_divide(SqlDecimal x, SqlDecimal y)
Поискав в Интернете, я обнаружил множество проблем, связанных с ошибками округления при преобразовании SqlDecimal в Decimal, но в моем случае все SqlDecimal от начала до конца, поскольку я хотел избежать именно этого. Точно так же я бы «надеялся», что результат будет идентичен тому, как это было изначально реализовано в SQL... но, увы. Даже когда он не переполняется, он даст мне разные результаты в «пограничных случаях».
У кого-нибудь есть какие-либо советы о том, как это исправить или, по крайней мере, обойти это? Использует ли SqlDecimal внутренне десятичные числа (.Net) и, следовательно, не может втиснуть информацию в свои 3 байта?!?!?
PS: я не могу предсказать, какие комбинации числовых (p, s) будут переданы функции, но 38,8 «приемлемо». Я надеялся сделать более быструю SQLCLR-версию моего исходного UDF (который делает гораздо больше, чем просто делит btw =). По общему признанию, это быстрее, но это должно работать для того же диапазона чисел.
DECIMAL(38, 8)
. Имеет смысл вернуть этот тип или, возможно,DECIMAL(38, 18)
, но определение входных параметров оказывает гораздо большее влияние на результат, чем возвращаемый тип. - person Solomon Rutzky   schedule 30.01.2017