got*_*tqn 9 .net c# sql sql-server sqlclr
创建自定义聚合函数时,需要指定枚举格式:
SqlUserDefinedTypeAttribute和SqlUserDefinedAggregateAttribute使用Format Enumeration来指示用户定义类型(UDT)或聚合的序列化格式.
当使用UserDefinedformat 时,您的类需要实现IBinarySerialize接口并覆盖它read和write方法.
我的问题是这些方法到底需要做什么?
看一下这些例子,我猜他们应该能够读/写聚合结果吗?
例如,我正在尝试创建一个连接不同数字的SQL CLR函数.在T-SQL中,我可以有1到255个不同的数字(TINYINT值).我需要从它们创建一个字符串(使用分隔符),但也要对数字进行排序.该函数似乎工作,但我不确定我是否按预期覆盖了方法:
[Serializable]
[
Microsoft.SqlServer.Server.SqlUserDefinedAggregate
(
Microsoft.SqlServer.Server.Format.UserDefined,
IsInvariantToNulls = true,
IsInvariantToDuplicates = true,
IsInvariantToOrder = false,
MaxByteSize = 1024
)
]
public class ConcatenateAnswersPos : Microsoft.SqlServer.Server.IBinarySerialize
{
private List<byte> intermediateResult;
public void Init()
{
intermediateResult = new List<byte>();
}
public void Accumulate(SqlByte value)
{
intermediateResult.Add((byte)value);
}
public void Merge(ConcatenateAnswersPos other)
{
intermediateResult.AddRange(other.intermediateResult);
}
public SqlString Terminate()
{
if (intermediateResult != null)
{
intermediateResult.Sort();
return new SqlString(string.Join(";", intermediateResult));
}
else
{
return new SqlString("");
}
}
public void Read(BinaryReader r)
{
if (r == null) throw new ArgumentNullException("r");
intermediateResult = new List<byte>();
string[] answers = r.ReadString().Split(';');
foreach (string answer in answers)
{
intermediateResult.Add(Convert.ToByte(answer));
}
}
public void Write(BinaryWriter w)
{
if (w == null) throw new ArgumentNullException("w");
intermediateResult.Sort();
w.Write(string.Join(";", intermediateResult));
}
}
Run Code Online (Sandbox Code Playgroud)
用户定义聚合(UDA)的任何特定实例都不保证在查询的整个生命周期中都存在.它需要有一个可存储的代表.当您只使用值类型时(如问题中的"枚举格式"链接所述),提供的Read和Write方法了解如何序列化和反序列化UDA,在这种情况下您将使用Format.Native.但是当你开始使用引用类型(字符串,集合,自定义类型等)时,由你来定义这些值如何被序列化和反序列化,在这种情况下你需要使用Format.UserDefined和覆盖Read和Write方法,这样你就可以进入控制那些行动.
需要序列化的值是将UDA的新实例恢复到序列化之前所处的确切状态所需的任何值.这意味着:不要依赖于运行的Init()方法(它每组运行一次!)或变量初始化器(它们每次实例化运行一次,并且UDA可以重用于多个组而无需重新创建!).因此,您需要序列化所有基值,即使它们与最终输出没有直接关系.
也就是说,你至少应该在@ Damien_The_Unbeliever的回答中做出优化:
不要在Write方法中进行排序.你已经在Terminate方法(适当的地方)做了,所以做两次是没用的,更不用说效率很低了.
存储集合的计数,然后存储各个元素
除此之外:
当你说你的UDA"连接不同的数字"时,如果你真的意味着"不同",那么你需要检查每个数字,看它是否已经在列表中.我怀疑这是你的愿望,因为你已经IsInvariantToDuplicates设定为真.您可以在Accumulate方法中执行此操作:
if (!intermediateResult.Contains(value.Value))
{
intermediateResult.Add(value.Value);
}
Run Code Online (Sandbox Code Playgroud)
在Merge方法中(涉及并行性时称为):
foreach (byte _NewValue in other.intermediateResult)
{
if (!intermediateResult.Contains(_NewValue))
{
intermediateResult.Add(_NewValue);
}
}
Run Code Online (Sandbox Code Playgroud)
请注意我改变你的演员(byte)value- 在Accumulate方法中使用该Value属性.所有的SqlTypes(如SqlByte,SqlString,SqlInt32,等)有Value返回,你会期望在.NET类型属性.这意味着,没有必要呼吁ToString()在SqlString尽可能多的人似乎做.
我会谨慎对待MaxByteSize1024.由于在一个字符串(当前方法)中保存"165; 207"在技术上是14个字节(每个字符7个字符*2个字节)而保存,因此实施@ Damien的建议可以部分减轻这种担忧.计数和单个字节只有6个字节(Int32用于存储count+ 2个字节的4个字节).而这种差异只是用于存储2个值.存200?Yeesh!
您没有指定IsNullIfEmpty属性.您需要指定它,特别是考虑到您的Terminate方法在内部集合时返回空字符串null.您应该添加,IsNullIfEmpty = false因为NULL如果永远不会调用它,您不想返回.
Terminate处理null集合的方法中的额外逻辑可能不是必需的.该集合在Init和Read方法中初始化,因此我不确定null在Terminate调用时它是如何成为的.
如果您想要一个创建用户定义聚合的示例Format.UserDefined,请查看充分利用SQL Server 2005 UDT和UDA(需要免费注册).我在SQL Server 2008问世之前编写过,它允许超过8000个字节被序列化,因此您可以忽略(目前)有关压缩数据以进行序列化的方面.
此外,如果您想了解更多关于SQLCLR的信息,我正在为SQL Server Central编写一系列文章:SQLCLR的阶梯(与第一个链接文章相同的网站).
| 归档时间: |
|
| 查看次数: |
1350 次 |
| 最近记录: |