SQL CLR 扩展的舍入误差(用户定义的聚合)

发布于 2024-10-19 13:59:13 字数 2156 浏览 3 评论 0原文

我正在为 SQL Server 2008 编写一些自定义 .Net 扩展。其中一个是用户定义的聚合,它将一组十进制数字聚合为一个十进制值。

为了缩小问题范围,我使用了一个简单的 Const 聚合,它只返回一个常量十进制值。当将此作为用户定义的聚合添加到 SQL Server 时,返回的值始终四舍五入:

SELECT dbo.Const(n, 2.5) from (select 1 n) x -- returns 3, not 2.5

我缺少什么?

代码如下:

using System;
using System.Data.SqlTypes;
using System.IO;
using Microsoft.SqlServer.Server;
using SqlServer.Clr.Extensions.Aggregates.Implementation;

[Serializable]
[SqlUserDefinedAggregate(Format.UserDefined,MaxByteSize = -1)]
public class Const : IBinarySerialize
{
    private decimal _constValue;

    public void Init() {}

    public void Accumulate(SqlDecimal value, SqlDecimal constValue)
    {
        _constValue = constValue.Value;
    }

    public void Merge(Const value) {}

    public SqlDecimal Terminate()
    {
        return new SqlDecimal(_constValue);
    }

    public void Read(BinaryReader r)
    {
        _constValue = r.ReadDecimal();
    }

    public void Write(BinaryWriter w)
    {
        w.Write(_constValue);
    }
}

将此代码编译为名为 SqlServer.Clr.Extensions.dll 的程序集。可以使用以下脚本将其添加到 SQL Server 并验证意外行为:

use [MyDb] -- replace with your db name
go

-- drop the Const aggregate if it exists
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[Const]'))
    DROP AGGREGATE [Const]
GO
-- drop the assembly if it exists
IF  EXISTS (SELECT * FROM sys.assemblies asms WHERE asms.name = N'SqlServer.Clr.Extensions' and is_user_defined = 1)
    DROP ASSEMBLY [SqlServer.Clr.Extensions]
GO

-- import the assembly
CREATE ASSEMBLY [SqlServer.Clr.Extensions]
AUTHORIZATION [dbo]
    FROM 'C:\Path\To\SqlServer.Clr.Extensions.dll'
WITH PERMISSION_SET = SAFE
GO

CREATE AGGREGATE [Const](@input decimal, @constValue decimal)
    RETURNS decimal
    EXTERNAL NAME [SqlServer.Clr.Extensions].[Const] -- put the Const class is in the global namespace
GO

SELECT dbo.Const(n, 2) from (select 1 n) x
SELECT dbo.Const(n, 2.5) from (select 1 n) x

I'm writing some custom .Net extensions for SQL Server 2008. One of them is a user-defined aggregate that should aggregate a set of decimal numbers to a decimal value.

To narrow down my problem, I'm using a simple Const aggregate, that simply returns a constant decimal value. When adding this as a user-defined aggregate to SQL Server, the values returned are always rounded:

SELECT dbo.Const(n, 2.5) from (select 1 n) x -- returns 3, not 2.5

What am I missing?

Here's the code:

using System;
using System.Data.SqlTypes;
using System.IO;
using Microsoft.SqlServer.Server;
using SqlServer.Clr.Extensions.Aggregates.Implementation;

[Serializable]
[SqlUserDefinedAggregate(Format.UserDefined,MaxByteSize = -1)]
public class Const : IBinarySerialize
{
    private decimal _constValue;

    public void Init() {}

    public void Accumulate(SqlDecimal value, SqlDecimal constValue)
    {
        _constValue = constValue.Value;
    }

    public void Merge(Const value) {}

    public SqlDecimal Terminate()
    {
        return new SqlDecimal(_constValue);
    }

    public void Read(BinaryReader r)
    {
        _constValue = r.ReadDecimal();
    }

    public void Write(BinaryWriter w)
    {
        w.Write(_constValue);
    }
}

Compile this code to an assembly named SqlServer.Clr.Extensions.dll. The following script can be used to add it to SQL Server and verify the unexpected behaviour:

use [MyDb] -- replace with your db name
go

-- drop the Const aggregate if it exists
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[Const]'))
    DROP AGGREGATE [Const]
GO
-- drop the assembly if it exists
IF  EXISTS (SELECT * FROM sys.assemblies asms WHERE asms.name = N'SqlServer.Clr.Extensions' and is_user_defined = 1)
    DROP ASSEMBLY [SqlServer.Clr.Extensions]
GO

-- import the assembly
CREATE ASSEMBLY [SqlServer.Clr.Extensions]
AUTHORIZATION [dbo]
    FROM 'C:\Path\To\SqlServer.Clr.Extensions.dll'
WITH PERMISSION_SET = SAFE
GO

CREATE AGGREGATE [Const](@input decimal, @constValue decimal)
    RETURNS decimal
    EXTERNAL NAME [SqlServer.Clr.Extensions].[Const] -- put the Const class is in the global namespace
GO

SELECT dbo.Const(n, 2) from (select 1 n) x
SELECT dbo.Const(n, 2.5) from (select 1 n) x

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

牵强ㄟ 2024-10-26 13:59:13

您必须将 @constvaluedecimal 更改为 @constvaluedecimal(13,2)

Create aggregate [const](@input decimal, @constvalue decimal)

You have to change @constvalue decimal to @constvalue decimal(13,2) in

Create aggregate [const](@input decimal, @constvalue decimal)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文