I have trouble understanding of how CLR User-Defined Aggregates work.
I have to create some custom CLR aggregates with multiple parameters. The point is to get the value
There's no need to store a list of all the records - you only need to store the details of the oldest record you've seen so far.
Something like this should work:
[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);
}
}
}
If you are looking for an implementation of your specific request, then @Richard's answer looks to be correct (though, you might still need to implement the Read
and Write
methods for using a custom type -- Format.UserDefined
).
However, it seems from the comments on the question that this is more of a general question of when to do processing of whatever information you are collecting. In that case:
The Accumulate
method is called for every row in a particular GROUP. This is the entry point.
The Merge
method is called when parallelism is being used. SQL Server uses this method to combine the information from various threads. Depending on the type of algorithm you are doing, here you might: combine the current and incoming information, decide to keep the current info or the incoming info (as is being done in @Richard's implementation), recalculate the current info based on the new incoming info.
The Terminate
method is called at the end of each particular GROUP. Here is where you would do the final calculation / logic and then return the expected result.
This information, and more, can be found on the MSDN page for Requirements for CLR User-Defined Aggregates.