1

为了获得排序后的聚合字符串,我在下面编写了 CLR 函数。但是,它总是返回空而不是我预期的,就像“001,002,003”一样。我试图在visual studio 2017中调试CLR函数,但抛出了错误信息

操作无法完成。未指定的错误

代码:

[Serializable]
[SqlUserDefinedAggregate(
    Format.UserDefined, //use clr serialization to serialize the intermediate result
    Name = "CLRSortedCssvAgg", //aggregate name on sql
    IsInvariantToNulls = true, //optimizer property
    IsInvariantToDuplicates = false, //optimizer property
    IsInvariantToOrder = false, //optimizer property
    IsNullIfEmpty = false, //optimizer property
    MaxByteSize = -1) //maximum size in bytes of persisted value
]

public class SortedCssvConcatenateAgg : IBinarySerialize
{
    /// <summary>
    /// The variable that holds all the strings to be aggregated.
    /// </summary>
    List<string> aggregationList;

    StringBuilder accumulator;

    /// <summary>
    /// Separator between concatenated values.
    /// </summary>
    const string CommaSpaceSeparator = ", ";

    /// <summary>
    /// Initialize the internal data structures.
    /// </summary>
    public void Init()
    {
        accumulator = new StringBuilder();
        aggregationList = new List<string>();
    }

    /// <summary>
    /// Accumulate the next value, not if the value is null or empty.
    /// </summary>
    public void Accumulate(SqlString value)
    {
        if (value.IsNull || String.IsNullOrEmpty(value.Value))
        {
            return;
        }

        aggregationList.Add(value.Value);
    }

    /// <summary>
    /// Merge the partially computed aggregate with this aggregate.
    /// </summary>
    /// <param name="other"></param>
    public void Merge(SortedCssvConcatenateAgg other)
    {
        aggregationList.AddRange(other.aggregationList);
    }

    /// <summary>
    /// Called at the end of aggregation, to return the results of the aggregation.
    /// </summary>
    /// <returns></returns>
    public SqlString Terminate()
    {
        if (aggregationList != null && aggregationList.Count > 0)
        {
            aggregationList.Sort();
            accumulator.Append(string.Join(CommaSpaceSeparator, aggregationList));
            aggregationList.Clear();
        }

        return new SqlString(accumulator.ToString());
    }

    public void Read(BinaryReader r)
    {
        accumulator = new StringBuilder(r.ReadString());
    }

    public void Write(BinaryWriter w)
    {
        w.Write(accumulator.ToString());
    }
}
4

2 回答 2

2

你很亲密。只需要一些小的调整。执行以下操作,它将起作用(我对其进行了测试):

  1. 删除所有对accumulator. 它不被使用。

  2. 将 Terminate()、Read() 和 Write() 方法替换为以下内容:

    public SqlString Terminate()
    {
        string _Aggregation = null;
    
        if (aggregationList != null && aggregationList.Count > 0)
        {
            aggregationList.Sort();
            _Aggregation = string.Join(CommaSpaceSeparator, aggregationList);
        }
    
        return new SqlString(_Aggregation);
    }
    
    public void Read(BinaryReader r)
    {
        int _Count = r.ReadInt32();
        aggregationList = new List<string>(_Count);
    
        for (int _Index = 0; _Index < _Count; _Index++)
        {
            aggregationList.Add(r.ReadString());
        }
    }
    
    public void Write(BinaryWriter w)
    {
        w.Write(aggregationList.Count);
        foreach (string _Item in aggregationList)
        {
            w.Write(_Item);
        }
    }
    

也就是说,我不确定这种方法是否比这种方法更快或更慢FOR XML,但 UDA 肯定会产生更易读的查询,尤其是在您需要多个聚合的情况下。

不过,我应该提一下,从 SQL Server 2017 开始,这变成了一个内置函数:STRING_AGG(它允许通过WITHIN GROUP (ORDER BY ... )子句进行排序)。

于 2018-09-19T06:26:23.257 回答
0

In your Accumulate and Merge, you're dealing with your aggregationList; in Read and Write you're dealing with accumulator. You should pick one or the other for all of them and use it. As I understand it, Read and Write are used when the engine needs to persist temporary results to a work table. For your case, when it does that, it's persisting only your empty StringBuilder.

于 2018-05-03T14:58:34.717 回答