3

I have a custom aggregation (UDA) and I want to pass an array of arguments like so:

select dbo.MyAggregation(
    t.type, 
    N'My aggregation property',
    t.param1, t.param2, t.param3
) 
from someTable t
group by t.type

The above is just an example, and I want to pass arbitrary number of params (i.e 4-...).

I've tried to use SqlString as an "array" with some delimiter, and then split the string by this delimiter, but this doesn't seem like a good solution.

4

The SQLCLR API does not allow for either overloading of methods nor the params keyword.

To pass in a variable number of parameters, you have a few options:

  1. Concatenate them into a delimited string that can be split within the .NET code (this is your current solution)
  2. Create a User-Defined Type (UDT) that is effectively just a wrapper to a simple array / generic list / dictionary (whichever is appropriate for your use- case). The UDT can have an "internal" method / property that is not exposed to the SQLCLR API but returns the native .NET collection such that you can get the values out of it directly. You would specify the name of the .NET struct / class of the UDT in the UDA signature instead of a Sql* type. This option allows for a variable number of elements (i.e. "parameters) to be passed in, but they will not be strongly typed (well, unless they are naturally all of the same type on the T-SQL side to begin with).
  3. If you have a relatively small and finite number of parameters to pass in, and they are of different types, you can create a UDT that has SQLCLR-exposed properties which are each one of the desired "parameters". Like suggestion #2 directly above, you would use this UDT as the parameter type of the UDA and then access its properties directly in the UDA code to get the various "parameter" values. For example (partial example code!, not full solution):

    internal string _Param1;
    internal int _Param2;
    
    public SqlString Param1
    {
        get
        {
            if (IsNull)
            {
                return SqlString.Null;
            }
            else
            {
                return new SqlString(_Param1);
            }
        }
    
        set
        {
            if (!value.IsNull)
            {
                _Param1 = value.Value;
            }
        }
    }
    
    public SqlInt32 Param2
    {
        get
        {
            if (IsNull)
            {
                return SqlInt32.Null;
            }
            else
            {
                return new SqlInt32(_Param2);
            }
        }
    
        set
        {
            if (!value.IsNull)
            {
                _Param2 = value.Value;
            }
        }
    }
    

    Usage:

    DECLARE @Params dbo.TypeParams;
    SET @Params.Param1 = N'some string';
    SET @Params.Param2 = 35;
    
    SELECT dbo.MyAggregation(
               t.type, 
               N'My aggregation property',
               @Params
           ) 
    FROM  someTable t
    GROUP BY t.type;
    

    In your .NET method for MyAggregation:

    [Serializable]
    [SqlUserDefinedAggregate(Format.UserDefined)]
    public class MyAggregation : IBinarySerialize
    {
        private someType _SomeVariable;
    
        public void Init()
        {
            _SomeVariable = someValue;
        }
    
        public void Accumulate(SqlInt32 TheType, SqlString TheProperty,
                               typeNameOfUDT Params)
        {
            if (TheType.IsNull)
            {
                return;
            }
    
            SomeMethod(TheType.Value, Params._Param1, Params._Param2);
        }
    
        ...        
    }
    

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.