SQL Server中的SQL group_concat函数[重复]

Posted

技术标签:

【中文标题】SQL Server中的SQL group_concat函数[重复]【英文标题】:SQL group_concat function in SQL Server [duplicate] 【发布时间】:2012-02-10 17:18:08 【问题描述】:

如果有一个表叫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这可能吗?

【问题讨论】:

哪个 SQL Server 版本?你看***.com/questions/451415/… 我正在使用 sql server 2008 @ BartekR @OlegDok 因为我是 sql 新手,我已经尝试过该帖子,但我对其中的 column_names 感到困惑..:( 【参考方案1】:
    用于 XML 路径 trick 和 article CLR 用户定义聚合 对于 sql server 2005 之前的版本 - 临时表

#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

如何去掉最后的逗号 - 是你自己的

#2 的 CLR 聚合 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
    

【讨论】:

精湛的临时表工作...Thnx...:)..现在我必须努力排除最后一个逗号..【参考方案2】:

@OlegDok's 选择的答案可能会返回正确的结果。但性能可能很糟糕。这个测试场景将说明它。

创建临时表:

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

在这种情况下,Distinct 不是清理行的正确方法。 为避免这种笛卡尔连接,请在加入之前减少初始 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 秒

【讨论】:

【参考方案3】:

我认为 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.

【讨论】:

【参考方案4】:

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

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

【讨论】:

以上是关于SQL Server中的SQL group_concat函数[重复]的主要内容,如果未能解决你的问题,请参考以下文章

如何使用sql server数据库中的标量值函数

如何查看sql server 2008的SQL语句执行错误日志

使用 SQLBulkCopy - SQL Server 2016 中的表比 SQL Server 2014 中的表大得多

SQL Server如何防止动态sql中的sql注入

本地 SQL Server 实例和 Azure SQL Server 中的 LoadData 脚本

sqlserver之[SQL Server]缓冲池中的可用内存不足。[Microsoft][ODBC SQL Server Driver][SQL Server]语句已终止