在varbinary数据类型sql server上执行聚合操作

时间:2014-09-11 10:10:32

标签: sql sql-server

我需要在sql server中的varbinary列上执行聚合函数。下面的代码片段给出了数据类型varbinary(max)无效的错误。

  DECLARE @vbtest TABLE(  
  al varbinary(MAX)  
  )
  insert into @vbtest values(0x0000000000000004)
  insert into @vbtest values(0x0000000000000006)
  insert into @vbtest values(0x0000000000000008)
  select sum(al) from @vbtest

有没有办法对varbinary数据类型执行聚合操作。

注意:我无法转换为int / Bigint,因为我的varbinary值大于bigint数据类型。

2 个答案:

答案 0 :(得分:1)

您需要创建CLR聚合UDF。以下是一些例子:

Create CLR function

SQL CLR Binary CLR Aggregate

这是我写的一个涉及Bitwise OR操作的例子:

[Serializable]
[Microsoft.SqlServer.Server.SqlUserDefinedAggregate(Format.UserDefined,
    IsInvariantToDuplicates = false,
    IsInvariantToNulls = true,
    IsInvariantToOrder = true,
    IsNullIfEmpty = true,
    MaxByteSize = -1, // -1 represents values above 8000 bytes up to 2 GB.
    Name = "udfClrAggVarbinaryBitwiseOR")]
public struct udfClrAggVarbinaryBitwiseOR : IBinarySerialize
{
    /// <summary>
    ///   Deserialize from the reader to recreate the struct
    /// </summary>
    /// <param name = "r">BinaryReader</param>
    public void Read(BinaryReader r)
    {
        this.Bytes = r.ReadBytes((int)r.BaseStream.Length);
    }

    /// <summary>
    ///   Serialize the struct.
    /// </summary>
    /// <param name = "w">BinaryWriter</param>
    public void Write(BinaryWriter w)
    {
        w.Write(this.Bytes);
    }

    /// <summary>
    /// Used to compute bitwise operations.
    /// </summary>
    public byte[] Bytes { get; set; }

    /// <summary>
    /// Used to inform if the accumulation has received values
    /// </summary>
    public bool HasValue { get; private set; }

    public void Init()
    {
        this.Bytes = new byte[4]; // this was just arbitrarily set for starting values.
        this.HasValue = false;
    }

    public void Accumulate(SqlBinary Value)
    {
        if (Value.IsNull)
        {
            // do nothing.
        }
        else
        {
            byte[] bytes = (byte[]) Value;
            if (!HasValue)
            {
                // if this is the first value received, take it.
                this.Bytes = bytes;
            }
            else
            {
                // otherwise, compute the bitwise OR operation.
                this.Bytes = bytes.Bin_Or(this.Bytes); // The Bin_Or extension method was defined in the link below.

            }
            this.HasValue = true;
        }
    }

    public void Merge(udfClrAggVarbinaryBitwiseOR Group)
    {
        if (Group.HasValue)
        {
            this.Bytes = Group.Bytes.Bin_Or(this.Bytes);
        }
    }

    public SqlBinary Terminate()
    {
        // Put your code here
        //if (HasValue)
        //{
            return this.Bytes;
        //}
        //else
        //{
        //    return System.Data.SqlTypes.SqlBinary.Null;
        //}
    }
}

我使用了Bin_Or的代码:Binary operations on byte arrays, with parallelism and pointers

在实现自定义序列化时,必须将SqlUserDefinedAggregate属性设置为Format.UserDefined(即不是默认的Format.Native),并且必须通过实现IBinarySerialize手动设置序列化。我在下面提供一个例子。

除非直接从Visual Studio数据库项目正确设置数据库部署,否则部署CLR功能可能会很麻烦。 (您将需要首先安装SQL数据工具。)关键是在项目设置下正确设置Visual Studio数据库项目中的数据库设置 - &gt;数据库设置 - &gt;其他 - &gt;设置Trustworthy = true。 我使用的是Visual Studio 2015和SQL Server 2014.(我还需要在项目属性中设置我的.NET版本 - &gt; SQLCLR - &gt;将目标框架设置为4.5.2以使其从VS 2015部署到SQL Server 2014。)

然后,要设置发布,请右键单击您的数据库项目,然后选择“发布数据库”。在“高级发布设置”中,选中&#34;始终重新创建数据库。&#34; (确保在部署之前备份数据库!!!)为此,您需要一个包含数据的SQL脚本,并将其作为部署的一部分包含在内。

要生成脚本,请在SQL Server Management Studio中右键单击您的数据库,然后转到任务 - &gt;生成脚本...... - &gt; (脚本整个数据库) - &gt;下一步 - &gt;高级 - &gt;一般 - &gt;脚本的数据类型=仅数据。然后保存到新的sql脚本文件。 然后将此现有文件添加到Visual Studio数据库项目,并在Visual Studio的脚本属性中,将Build Action设置为PostDeploy。这可确保在发布过程中重新创建数据库后加载数据。

要进行调试,您必须启用调试并确保防火墙已充分打开。要进行调试,您还需要以管理员身份运行VS,并在Visual Studio SQL Server对象资源管理器中右键单击您的服务器实例,然后选择“允许SQL / CLR调试”。&#34;这需要在VS每次启动时完成。 (这有点烦人,但我还没有找到更好的方法。)

在我提供的示例中,我还需要在SQLCLR项目设置中将权限级别设置为UNSAFE,我需要在单独的C#项目中添加Bin_op函数的类;然后,我编译了该项目(也作为.NET 4.5.2),将该项目添加为我的数据库项目的引用,并标记了程序集引用属性Permission Set = Unsafe。

答案 1 :(得分:0)

使用SUBSTRING()。快速举例:

-- get the actual data
DECLARE @tmp varbinary(MAX) = (SELECT TOP 1 bTemplate
FROM TB_USER_TEMPLATE ORDER BY nUserIdn, nIndex, nFingerIndex)
DECLARE @sum int = (SELECT TOP 1 nTemplatecs
FROM TB_USER_TEMPLATE ORDER BY nUserIdn, nIndex, nFingerIndex)

-- begin calc using 
DECLARE @i int = 0, @calc int = 0
WHILE @i < LEN(@tmp)
BEGIN
    SET @calc += CAST(SUBSTRING(@tmp, @i + 1, 1) AS int)
    SET @i += 1
END

SELECT @sum, @calc -- should be equal