Имам някои неочаквани резултати, когато работя с SqlDecimals. В крайна сметка изглежда се свежда до проблеми с мащаба при разделянето на 2 SqlDecimals.
c# примерен код изглежда така:
[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: Не мога да предвидя какви комбинации от numeric(p,s) ще бъдат предадени на функцията, но 38,8 е „приемливо“. Надявах се да направя по-бърза SQLCLR версия на моя оригинален UDF (който прави много повече от просто разделяне между другото =). Вярно е, че е по-бърз, но трябва да работи за същия диапазон от числа.
DECIMAL(38, 8)
на първо място. Напълно логично е да върнете този тип, или евентуалноDECIMAL(38, 18)
, но дефиницията на входните параметри има много по-голямо влияние върху резултата, отколкото върнатият тип. - person Solomon Rutzky   schedule 30.01.2017