der*_*oby 7 c# sql-server sqlclr
看来我正在用'我最喜欢的数据类型'SqlDecimal遇到更多困境.我想知道这是否应该被认为是一个错误.
当我在SQL中乘以两个小数字时,我得到了预期的结果.当我通过SQLCLR函数运行相同的数字时,结果非常令人惊讶.
c#代码:
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
namespace TestMultiplySQLDecimal
{
public static class Multiplier
{
[SqlFunction(DataAccess=DataAccessKind.None, IsDeterministic = true,IsPrecise = true)]
public static SqlDecimal Multiply(SqlDecimal a, SqlDecimal b)
{
if (a.IsNull || b.IsNull) return SqlDecimal.Null;
return a*b;
}
}
}
Run Code Online (Sandbox Code Playgroud)
SQL代码:
USE tempdb
GO
IF DB_ID('test') IS NOT NULL DROP DATABASE test
GO
CREATE DATABASE test
GO
USE test
GO
CREATE ASSEMBLY TestMultiplySQLDecimal
FROM 'C:\Users\tralalalaa\Documents\visual studio 2015\Projects\TestMultiplySQLDecimal\TestMultiplySQLDecimal\bin\Release\TestMultiplySQLDecimal.dll'
WITH PERMISSION_SET = SAFE
GO
CREATE FUNCTION dbo.fn_multiply(@a decimal(38,8), @b decimal(18,8))
RETURNS decimal(38,8)
EXTERNAL NAME TestMultiplySQLDecimal.[TestMultiplySQLDecimal.Multiplier].Multiply
GO
DECLARE @a decimal(38, 8),
@b decimal(18, 8),
@c decimal(38, 8),
@f decimal(38, 8)
SELECT @a = -0.00000450,
@b = 0.193,
@c = NULL,
@f = NULL
SELECT @c = @a * @b,
@f = dbo.fn_multiply(@a, @b)
SELECT multiply = null, c = @c, f = @f
Run Code Online (Sandbox Code Playgroud)
结果是:c = -0.00000100 f = +0.00000100
我知道"绝对"的差异是"最小的"而且我"淡化"了更大的错误,将其归咎于"四舍五入的差异"......但是很难向客户解释消极时期的积极结果是积极的.毕竟,T-SQL支持它很好......
我可以尝试使用十进制(28,8)而不是十进制(38,8)解决它,但我会遇到其他(完全不相关)的问题然后=/
以下控制台应用程序表现出相同的问题,而不必涉及SQL Server/SQLCLR:
using System;
using System.Data.SqlTypes;
namespace PlayAreaCSCon
{
class Program
{
static void Main(string[] args)
{
var dec1 = new SqlDecimal(-0.00000450d);
var dec2 = new SqlDecimal(0.193d);
dec1 = SqlDecimal.ConvertToPrecScale(dec1, 38, 8);
dec2 = SqlDecimal.ConvertToPrecScale(dec2, 18, 8);
Console.WriteLine(dec1 * dec2);
Console.ReadLine();
}
}
}
Run Code Online (Sandbox Code Playgroud)
打印 0.000001
我相信这个漏洞大约位于1550SqlDecimal行左右:
ret = new SqlDecimal(rgulRes, (byte)culRes, (byte)ResPrec,
(byte)ActualScale, fResPositive);
if (ret.FZero ())
ret.SetPositive();
ret.AssertValid();
ret.AdjustScale(lScaleAdjust, true);
return ret;
Run Code Online (Sandbox Code Playgroud)
它首先使用final scale参数构造一个新的小数.接下来根据传入的构造函数参数检查结果是否为"零".
然后,在断言所有内容都有效后,它会执行比例调整.
在执行FZero检查时,结果类似于-0.0000008685.我们知道最终的比例将是6,因为我们在规模和精度上达到了极限.那么,前6位数都是零.
只有在此之后,当调整比例时,才需要考虑舍入并将其移动1到最后的小数位.
这是一个错误.遗憾的是,SQL Server本机实现的源代码decimal不公开,因此我们无法将其与托管实现进行比较,SqlDecimal以查看它们的相似程度以及原始程序如何避免相同的错误.
| 归档时间: |
|
| 查看次数: |
213 次 |
| 最近记录: |