我已经创建并使用了很多次SQL CLR聚合来连接值-它也按指定的数字对值进行排序,并使用用户输入分隔符来连接它们。

我在大量数据上使用了相同的聚合,并且注意到未使用分隔符-值是串联在一起的,但没有分隔符。

经过大量测试,我发现在Terminate方法中,分隔符丢失/未读取。我使用硬编码分隔符仔细检查了这一点-一切正常。

我猜我的ReadWrite方法(在处理大量数据时使用)有问题,但是无法理解。

这是功能代码:

[Serializable]
[
    Microsoft.SqlServer.Server.SqlUserDefinedAggregate
    (
        Microsoft.SqlServer.Server.Format.UserDefined,
        IsInvariantToNulls = true,
        IsInvariantToDuplicates = false,
        IsInvariantToOrder = false,
        IsNullIfEmpty = false,
        MaxByteSize = -1
    )
]
/// <summary>
/// Concatenates <int, string, string> values defining order using the specified number and using the given delimiter
/// </summary>
public class ConcatenateWithOrderAndDelimiter : Microsoft.SqlServer.Server.IBinarySerialize
{
    private List<Tuple<int, string>> intermediateResult;
    private string delimiter;
    private bool isDelimiterNotDefined;

    public void Init()
    {
        this.delimiter = ",";
        this.isDelimiterNotDefined = true;
        this.intermediateResult = new List<Tuple<int, string>>();
    }

    public void Accumulate(SqlInt32 position, SqlString text, SqlString delimiter)
    {
        if (this.isDelimiterNotDefined)
        {
            this.delimiter = delimiter.IsNull ? "," : delimiter.Value;
            this.isDelimiterNotDefined = false;
        }

        if (!(position.IsNull || text.IsNull))
        {
            this.intermediateResult.Add(new Tuple<int, string>(position.Value, text.Value));
        }
    }

    public void Merge(ConcatenateWithOrderAndDelimiter other)
    {
        this.intermediateResult.AddRange(other.intermediateResult);
    }

    public SqlString Terminate()
    {
        this.intermediateResult.Sort();
        return new SqlString(String.Join(this.delimiter, this.intermediateResult.Select(tuple => tuple.Item2)));
    }

    public void Read(BinaryReader r)
    {
        if (r == null) throw new ArgumentNullException("r");

        int count = r.ReadInt32();
        this.intermediateResult = new List<Tuple<int, string>>(count);

        for (int i = 0; i < count; i++)
        {
            this.intermediateResult.Add(new Tuple<int, string>(r.ReadInt32(), r.ReadString()));
        }

        this.delimiter = r.ReadString();
    }

    public void Write(BinaryWriter w)
    {
        if (w == null) throw new ArgumentNullException("w");

        w.Write(this.intermediateResult.Count);

        foreach (Tuple<int, string> record in this.intermediateResult)
        {
            w.Write(record.Item1);
            w.Write(record.Item2);
        }

        w.Write(this.delimiter);
    }
}

最佳答案

Merge()方法仅在使用并行性且特定组分布在多个线程中时才被调用。在这种情况下,已调用Init(),并且0个或更多实例Accumulate()

因此,在并行性的情况下,如果已调用Init()但尚未调用Accumulate(),则delimiter中的值将是在Init()方法中设置的值。问题中的代码表明它已设置为,,但我怀疑稍后在试图弄清楚这一点时已添加了该代码。当然,这假定逗号作为分隔符传递到Accumulate()中。或者,也许总是在Init()中将逗号设置为默认值,但是另一个字符是通过Accumulate()传入的,并且该字符没有通过最终输出(问题中未显示对UDA的特定调用,是不正确的输出,因此此处存在一些歧义)。

尽管另一个答案中显示的修复程序似乎可以正常工作,但鉴于可能存在这样的情况,即当前对象至少被调用一次Accumulate(),但是“其他”对象已合并到该对象中,因此这不是通用解决方案仍然为空(也许没有匹配的行,或者某些其他原因导致在调用Accumulate()时值未存储在本地)。在这种情况下,当前对象将具有所需的定界符,而“其他”对象仍将具有默认定界符。理想的解决方案是将isDelimiterNotDefined的值也存储在Write()方法中,再次在Read()方法中将其取回,然后将本地值与other.isDelimiterNotDefinedMerge()方法中进行比较,以便您可以确定是否应保留delimiter的本地值或其他值(取决于设置/定义的值)。

10-06 12:39