User-Defined Aggregate in SQL Server 2008 - How to deploy with MaxByteSize = -1?
-
05-07-2019 - |
Question
I read here (and elsewhere) that it's possible, in SQL Server 2008, to build a user-defined aggregate which can return a string longer than 8000 characters. This is exactly what I need.
Supposedly, the method is to set maxByteSize to -1 instead of a number btw 1 and 8000; this should allow any size up to 2GB.
For some reason, apparently, you can't deploy straight from Visual Studio 2008 if you use this setting; so you need to manually deploy.
So: I build my project - GroupConcat (which is supposed to simulate MySQL's group_concat aggregator) - which gives me, in the project's bin folder, a file "SqlClassLibrary.dll". Per the instructions on the above-linked page, I build the assembly in SQL Server. The command executes successfully. However, when I try to actually use the groupconcat aggregator:
select department, dbo.groupconcat(projectNumber) from projectleads group by department
...it says it can't be found. This all works fine if I set maxByteSize to 8000 and deploy directly from within VS2008, but I need >8000. Anybody know what I'm doing wrong?
Thanks -dan
NOTE: I do specifically need to have a groupconcat aggregator function rather than using some of the SQL Server tricks I've often seen.
Solution 2
Figured it out... After building the solution in Vis Studio, assuming I've dropped the .dll it creates into c:\temp and called it GroupConcat.dll:
CREATE ASSEMBLY GroupConcat from 'C:\temp\GroupConcat.dll' with permission_set = safe
GO
CREATE AGGREGATE groupconcat(@input nvarchar(max))
RETURNS nvarchar(max)
EXTERNAL NAME GroupConcat
GO
That does it.
OTHER TIPS
Alternately, you can use MaxSize
property of SqlFacetAttribute
to to indicate the varchar size. Note that in the example below I applied this attribute to the SqlString
parameters in the Accumulate
method and to the return value of the Terminate
method. This results in the following SQL signature:
AGGREGATE [dbo].[Concatenate] (@value nvarchar(max), @order int, @seperator nvarchar(max)) RETURNS nvarchar(max)
[Serializable]
[SqlUserDefinedAggregate(
Format.UserDefined,
IsInvariantToOrder = true,
IsInvariantToNulls = true,
IsInvariantToDuplicates = false,
IsNullIfEmpty = false,
MaxByteSize = -1)]
public struct Concatenate : IBinarySerialize
{
public void Init();
public void Accumulate([SqlFacet(MaxSize = -1)] SqlString value,
SqlInt32 order,
[SqlFacet(MaxSize = -1)] SqlString seperator);
public void Merge(Concatenate group);
[return: SqlFacet(MaxSize = -1)]
public SqlString Terminate();
public void Read(BinaryReader r);
public void Write(BinaryWriter w);
}
I don't know if this is any more "correct" than what you ended up doing, but it seems more natural.