SQL Server中的SQL group_concat函数

时间:2012-01-15 09:03:58

标签: sql-server-2008 asp-classic

如果有一个名为employee

的表
EmpID           EmpName
----------      -------------
1               Mary
1               John
1               Sam
2               Alaina
2               Edward

结果我需要这种格式:

EmpID           EmpName
----------      -------------
1               Mary, John, Sam
2               Alaina, Edward

问:此记录位于同一Employee表中。我几乎没有使用UDF,存储过程的经验,我需要通过查询完成这个事情。这可能不使用UDF,SP。

4 个答案:

答案 0 :(得分:31)

  1. FOR XML PATH trickarticle
  2. CLR用户定义汇总
  3. 用于sql server早期版本2005 - 临时表
  4. #1

    的示例
    DECLARE @t TABLE (EmpId INT, EmpName VARCHAR(100))
    INSERT @t VALUES
    (1, 'Mary'),(1, 'John'),(1, 'Sam'),(2, 'Alaina'),(2, 'Edward')
    SELECT distinct
        EmpId,
        (
            SELECT EmpName+','
            FROM @t t2
            WHERE t2.EmpId = t1.EmpId
            FOR XML PATH('')
        ) Concatenated
    FROM @t t1
    

    如何删除最终的逗号 - 是您自己的

    CLR聚合#2

    的c#代码
    using System;
    using System.Collections.Generic;
    using System.Data.SqlTypes;
    using System.Text;
    using Microsoft.SqlServer.Server;
    using System.IO;
    
    namespace DatabaseAssembly
    {
        [Serializable]
        [SqlUserDefinedAggregate(Format.UserDefined,
            IsInvariantToNulls = true,
            IsInvariantToDuplicates = true,
            IsInvariantToOrder = true,
            MaxByteSize = -1)]
        public struct StringJoin : IBinarySerialize
        {
            private Dictionary<string, string> AggregationList
            {
                get
                {
                    if (_list == null)
                        _list = new Dictionary<string, string>();
                    return _list;
                }
            }
            private Dictionary<string, string> _list;
    
            public void Init()
            {
    
            }
    
            public void Accumulate(SqlString Value)
            {
                if (!Value.IsNull)
                    AggregationList[Value.Value.ToLowerInvariant()] = Value.Value;
    
            }
    
            public void Merge(StringJoin Group)
            {
                foreach (var key in Group.AggregationList.Keys)
                    AggregationList[key] = Group.AggregationList[key];
            }
    
            public SqlChars Terminate()
            {
                var sb = new StringBuilder();
                foreach (var value in AggregationList.Values)
                    sb.Append(value);
                return new SqlChars(sb.ToString());
            }
    
            #region IBinarySerialize Members
    
            public void Read(System.IO.BinaryReader r)
            {
    
                try
                {
                    while (true)
                        AggregationList[r.ReadString()] = r.ReadString();
                }
                catch (EndOfStreamException)
                {
    
                }
            }
    
            public void Write(System.IO.BinaryWriter w)
            {
                foreach (var key in AggregationList.Keys)
                {
                    w.Write(key);
                    w.Write(AggregationList[key]);
                }
            }
    
            #endregion
        }
    }
    

答案 1 :(得分:13)

来自@ OlegDok的所选答案可能会返回正确的结果。但表现可能很糟糕。这个测试场景将说明它。

创建临时表:

CREATE table #temp (EmpId INT, EmpName VARCHAR(100))
;WITH N(N)AS 
(SELECT 1 FROM(VALUES(1),(1),(1),(1),(1),(1),(1),(1),(1),(1))M(N)),
tally(N)AS(SELECT ROW_NUMBER()OVER(ORDER BY N.N)FROM N,N a,N b,N c,N d,N e,N f)
INSERT #temp
SELECT EmpId, EmpName FROM (values(1, 'Mary'),(1, 'John'),(1, 'Sam')) x(EmpId, EmpName)
CROSS APPLY 
(SELECT top 2000 N FROM tally) y
UNION ALL
SELECT EmpId, EmpName FROM (values(2, 'Alaina'),(2, 'Edward')) x(EmpId, EmpName)
CROSS APPLY
(SELECT top 2000 N FROM tally) y

这只有10.000行。但很多相同的EmpId。

Oleg的答案中的这个查询在我的数据库上花了64秒。

SELECT distinct
    EmpId,
    (
        SELECT EmpName+','
        FROM #temp t2
        WHERE t2.EmpId = t1.EmpId
        FOR XML PATH('')
    ) Concatenated
FROM #temp t1

在这种情况下,区别不是清理行的正确方法。 要避免此笛卡尔联接,请在加入之前减少初始ID数。

这是处理此问题的正确方法:

;WITH CTE as
(
  SELECT distinct EmpId
  FROM #temp
)
SELECT 
    EmpId,
    STUFF((
        SELECT ','+EmpName
        FROM #temp t2
        WHERE t2.EmpId = t1.EmpId
        FOR XML PATH('')
    ), 1,1,'') Concatenated
FROM CTE t1

这需要不到1秒

答案 2 :(得分:0)

我认为MSSQL中没有GROUP_CONCAT函数。这个article显示了连接行值的不同方法。

当项目数量较少且预先知道时连接值

SELECT CategoryId,
       MAX( CASE seq WHEN 1 THEN ProductName ELSE '' END ) + ', ' +
       MAX( CASE seq WHEN 2 THEN ProductName ELSE '' END ) + ', ' +
       MAX( CASE seq WHEN 3 THEN ProductName ELSE '' END ) + ', ' +
       MAX( CASE seq WHEN 4 THEN ProductName ELSE '' END )
  FROM ( SELECT p1.CategoryId, p1.ProductName,
                ( SELECT COUNT(*) 
                    FROM Northwind.dbo.Products p2
                   WHERE p2.CategoryId = p1.CategoryId
                     AND p2.ProductName <= p1.ProductName )
           FROM Northwind.dbo.Products p1 ) D ( CategoryId, ProductName, seq )
 GROUP BY CategoryId ;

More ways on this link.

答案 3 :(得分:0)

这是开头给出的示例的解决方案:

SELECT DISTINCT emp_name,
STUFF(
(SELECT ', ' + RTRIM(proj_id)
FROM project_members AS t1 
WHERE t1.emp_name = t2.emp_name
FOR XML PATH (''))
, 1, 1, '')
FROM project_members t2