我需要在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数据类型。
答案 0 :(得分:1)
您需要创建CLR聚合UDF。以下是一些例子:
这是我写的一个涉及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