具有多个参数的SQLCLR自定义聚合

我无法理解CLR用户定义的聚合如何工作。

我必须创建一些具有多个参数的自定义CLR聚合。 关键是要根据第二个参数得到第一个参数的值。

例如,我的表中有以下值,我需要每个Type的最旧员工Name

  Type | Name | Age -------------------------------- Manager | emp 1 | 35 Manager | emp 2 | 42 Developer | emp 3 | 36 Developer | emp 4 | 45 Developer | emp 5 | 22 

所以我想写这样的查询来通过使用我的程序集得到结果:

 Select Type, dbo.fOldestEmployee(Name, Age) AS [Name] From xxx Group By Type 

这会回应:

  Type | Name ---------------------- Manager | emp 2 Developer | emp 4 

使用CLR用户定义聚合看起来很可能,但我很难找到这种实现的具体示例。

目前我有这个。 我创建了一个类来收集数据,但我怎样才能对它们进行排序(或做其他事情)?

 using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Data.SqlTypes; using Microsoft.SqlServer.Server; using System.Text; using System.Collections; using System.IO; [Serializable] [SqlUserDefinedAggregate( Format.UserDefined, IsInvariantToOrder = false, // order changes the result IsInvariantToNulls = false, // nulls change the result IsInvariantToDuplicates = false, // duplicates change the result MaxByteSize = -1)] public struct sOlder { private List _datas; public void Init() { _datas = new List(); } public void Accumulate(SqlString valueField, SqlInt32 ValueInt) { if (!valueField.IsNull && !ValueInt.IsNull) { _datas.Add(new MyData { ValField = valueField.Value, ValInt = ValueInt.Value }); } } public void Merge (sOlder Group) { _datas.AddRange(Group._datas); } public SqlString Terminate () { //... } public class MyData { public String ValField { get; set; } public Int32 ValInt { get; set; } } } 

有任何想法吗 ?

无需存储所有记录的列表 – 您只需要存储到目前为止看到的最旧记录的详细信息。

像这样的东西应该工作:

 [Serializable] [SqlUserDefinedAggregate( Format.UserDefined, IsInvariantToOrder = true, IsInvariantToNulls = true, IsInvariantToDuplicates = true, MaxByteSize = -1)] public struct sOlder : IBinarySerialize { private struct MyData { public string Name { get; set; } public int? Age { get; set; } public int CompareTo(MyData other) { if (Age == null) return other.Age == null ? 0 : -1; if (other.Age == null) return 1; return Age.Value.CompareTo(other.Age.Value); } public static bool operator <(MyData left, MyData right) { return left.CompareTo(right) == -1; } public static bool operator >(MyData left, MyData right) { return left.CompareTo(right) == 1; } } private MyData _eldestPerson; public void Init() { _eldestPerson = default(MyData); } public void Accumulate(SqlString name, SqlInt32 age) { if (!name.IsNull && !age.IsNull) { var currentPerson = new MyData { Name = name.Value, Age = age.Value }; if (currentPerson > _eldestPerson) { _eldestPerson = currentPerson; } } } public void Merge (sOlder other) { if (other._eldestPerson > _eldestPerson) { _eldestPerson = other._eldestPerson; } } public SqlString Terminate() { return _eldestPerson.Name; } public void Write(BinaryWriter writer) { if (_eldestPerson.Age.HasValue) { writer.Write(true); writer.Write(_eldestPerson.Age.Value); writer.Write(_eldestPerson.Name); } else { writer.Write(false); } } public void Read(BinaryReader reader) { if (reader.ReadBoolean()) { _eldestPerson.Age = reader.ReadInt32(); _eldestPerson.Name = reader.ReadString(); } else { _eldestPerson = default(MyData); } } } 

如果您正在寻找特定请求的实现,那么@ Richard的答案看起来是正确的(但是,您可能仍需要实现使用自定义类型的ReadWrite方法 – Format.UserDefined )。

但是,从问题的评论中可以看出,这更像是一个关于何时处理您正在收集的任何信息的一般性问题。 在这种情况下:

  • 特定GROUP中的每一行调用Accumulate方法。 这是切入点。

  • 使用并行性时调用Merge方法。 SQL Server使用此方法组合来自各种线程的信息。 根据您正在执行的算法类型,您可以:在此处:结合当前信息和传入信息,决定保留当前信息或传入信息(正如在@ Richard的实现中所做的那样),根据新信息重新计算当前信息传入信息。

  • Terminate方法在每个特定GROUP的末尾调用。 您可以在此处执行最终计算/逻辑,然后返回预期结果。

可以在MSDN页面上找到此信息以及CLR用户定义聚合的要求 。