Flatten association table to multi-value column?

后端 未结 4 1102
旧时难觅i
旧时难觅i 2021-01-07 07:48

I have a table with just product ID\'s and category ID\'s (products can be in more than one category). How can I flatten the category ID\'s into a product column so I end us

4条回答
  •  借酒劲吻你
    2021-01-07 08:28

    I created an CLR aggregate function that takes a varchar column and returns all its values separated by commas. In other words, it joins several strings into a comma-separated list. I am sure its performance is way better than any T-Sql trick.

    As any aggregate function, it can be used in combination with group by. For example:

    SELECT id, name, desc, JoinStrings(CONVERT(VARCHAR(20), category_id))
    FROM product p
    INNER JOIN category_products c ON p.category_id = c.category_id
    GROUP BY id, name, desc
    

    Here's the C# code to create the CLR assembly into Sql Server 2008:

    using System;
    using System.Data;
    using System.Data.SqlClient;
    using System.Data.SqlTypes;
    using Microsoft.SqlServer.Server;
    
    
    [Serializable]
    [Microsoft.SqlServer.Server.SqlUserDefinedAggregate(Format.UserDefined, IsInvariantToDuplicates=false, IsInvariantToOrder=false, IsInvariantToNulls=true, MaxByteSize=-1)]
    public struct JoinStrings : IBinarySerialize
    {
        private char[] sb;
        private int pos;
        public void Init()
        {
            sb = new char[512000];
            pos = 0;
        }
    
        public void Accumulate(SqlString Value)
        {
            if (Value.IsNull) return;
            char[] src = Value.ToString().ToCharArray();
            Array.Copy(src, 0, sb, pos, src.Length);
            pos += src.Length;
            sb[pos] = ',';
            pos++;
        }
    
        public void Merge(JoinStrings Group)
        {
            Accumulate(Group.Terminate());
        }
    
        public SqlString Terminate()
        {
            if (pos <= 0) 
                return new SqlString();
            else
                return new SqlString(new String(sb, 0, pos-1));
        }
    
        public void Read(System.IO.BinaryReader r)
        {
            this.Init();
            pos = r.ReadInt32();
            r.Read(sb, 0, pos);
        }
    
        public void Write(System.IO.BinaryWriter w)
        {
            w.Write(pos);
            w.Write(sb, 0, pos);
        }
    }
    

    Here's the code to create the function (although deploying from Visual Studio should do it automatically):

    CREATE AGGREGATE [dbo].[JoinStrings]
    (@s [nvarchar](4000))
    RETURNS[nvarchar](max)
    EXTERNAL NAME [YouAssemblyName].[JoinStrings]
    

提交回复
热议问题