代码之家  ›  专栏  ›  技术社区  ›  OMG Ponies

CLR:多参数聚合,参数不在最终输出中?

  •  5
  • OMG Ponies  · 技术社区  · 14 年前

      SELECT [article_id]
             , dbo.GROUP_CONCAT(0, t.tag_name, ',') AS col
        FROM [AdventureWorks].[dbo].[ARTICLE_TAG_XREF] atx
        JOIN [AdventureWorks].[dbo].[TAGS] t ON t.tag_id = atx.tag_id
    GROUP BY article_id
    

    DISTINCT钻头工作正常,但在累积范围内工作。。。

    输出:

    article_id  |  col
    -------------------------------------------------
    1           |  a         a         b         c         
    

    更新:值之间的空间过大是因为列定义为NCHAR(10),所以输出中将出现10个字符。我犯了个愚蠢的错误。。。


    在马丁·史密斯的帮助下 Write(BinaryWriter w)

    public void Write(BinaryWriter w)
    {
        w.Write(list.Count);
        for (int i = 0; i < list.Count; i++ )
        {
            if (i < list.Count - 1)
            {
                w.Write(list[i].ToString() + delimiter);
            }
            else 
            {
                w.Write(list[i].ToString());
            }
        }
    }
    

    问题是:


    w.write 在FOR循环中调用?

    C代码:


    using System;
    using System.Data;
    using System.Data.SqlClient;
    using System.Data.SqlTypes;
    using Microsoft.SqlServer.Server;
    using System.Xml.Serialization;
    using System.Xml;
    using System.IO;
    using System.Collections;
    using System.Text;
    
    [Serializable]
    [SqlUserDefinedAggregate(Format.UserDefined, MaxByteSize = 8000)]
    public struct GROUP_CONCAT : IBinarySerialize
    {
        ArrayList list;
        string delimiter;
    
        public void Init()
        {
            list = new ArrayList();
            delimiter = ",";
        }
    
        public void Accumulate(SqlBoolean isDistinct, SqlString Value, SqlString separator)
        {
            delimiter = (separator.IsNull) ? "," : separator.Value ;
    
            if (!Value.IsNull)
            {
                if (isDistinct)
                {
                    if (!list.Contains(Value.Value))
                    {
                        list.Add(Value.Value);
                    }
                }
                else
                {
                    list.Add(Value.Value);
                }            
            }
        }
    
        public void Merge(GROUP_CONCAT Group)
        {
            list.AddRange(Group.list);
        }
    
        public SqlString Terminate()
        {
            string[] strings = new string[list.Count];
    
            for (int i = 0; i < list.Count; i++)
            {
                strings[i] = list[i].ToString();
            }
    
            return new SqlString(string.Join(delimiter, strings));
        }
    
        #region IBinarySerialize Members
    
        public void Read(BinaryReader r)
        {
            int itemCount = r.ReadInt32();
            list = new ArrayList(itemCount);
    
            for (int i = 0; i < itemCount; i++)
            {
                this.list.Add(r.ReadString());
            }
        }
    
        public void Write(BinaryWriter w)
        {
            w.Write(list.Count);
            foreach (string s in list)
            {
                w.Write(s);
            }
        }
        #endregion
    }
    
    2 回复  |  直到 14 年前
        1
  •  5
  •   agsamek    14 年前

    w.Write(delimiter)
    

    作为写入方法的第一行

    delimiter = r.ReadString();
    

    作为阅读方法的第一行。

    它没有。它只适用于您的测试场景。

    Write方法需要与Read方法兼容。如果你写两个字符串,只读一个,那么它就行不通了。这里的想法是您的对象可以从内存中删除,然后加载。这就是写和读应该做的。在您的情况下-这确实发生了,您无法保持对象值。

        2
  •  0
  •   Herman Kan    10 年前

    @agsamek给出的答案是正确的,但并不完整。查询处理器可以实例化多个聚合器,例如用于并行计算,以及在连续调用查询处理器之后最终保存所有数据的聚合器 Merge() 可以分配一个空记录集,即 Accumulate()

    var concat1 = new GROUP_CONCAT();
    concat1.Init();
    results = getPartialResults(1); // no records returned
    foreach (var result in results)
        concat1.Accumulate(result[0], delimiter); // never called
    ...
    var concat2 = new GROUP_CONCAT();
    concat2.Init();
    results = getPartialResults(2);
    foreach (var result in results)
        concat2.Accumulate(result[0], delimiter);
    ...
    concat1.Merge(concat2);
    ...
    result = concat1.Terminate();
    

    在这种情况下, concat1 的专用字段 delimiter 用于 Terminate() 保持默认状态 Init() 初始化()

    我不确定这是一个bug还是在以后的版本中修复过(我在SQLServer2008R2中偶然发现的)。我的解决方法是利用传入的其他组 合并() :

    public void Merge(GROUP_CONCAT Group)
    {
        if (Group.list.Count != 0) // Group's Accumulate() has been called at least once
        {
    
            if (list.Count == 0) // this Accumulate() has not been called
                delimiter = Group.delimiter;
    
            list.AddRange(Group.list);
        }
    }
    

    StringBuilder 而不是 ArrayList