SQL Server CLR функция агрегации отсортированной строки - PullRequest
0 голосов
/ 02 мая 2018

Чтобы получить отсортированную агрегированную строку, я написал функцию CLR ниже. Тем не менее, он всегда возвращает пустой вместо того, что я ожидал, как «001, 002, 003». Я попытался отладить функцию CLR в visual studio 2017, но выкинул сообщение об ошибке

Операция не может быть завершена. Неуказанная ошибка

Код:

[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());
    }
}

Ответы [ 2 ]

0 голосов
/ 19 сентября 2018

Вы рядом. Просто нужно несколько небольших корректировок. Сделайте следующее, и это будет работать (я проверял это):

  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 ... )).

0 голосов
/ 03 мая 2018

В ваших Accumulate и Merge вы имеете дело с вашим aggregationList; в Read и Write вы имеете дело с accumulator. Вы должны выбрать один или другой для всех из них и использовать его. Насколько я понимаю, Read и Write используются, когда движку необходимо сохранить временные результаты на рабочем столе. Для вашего случая, когда он это делает, он сохраняет только ваш пустой StringBuilder.

...