SQL Server中的SQL group_concat函数
如果有一个名为员工的表
EmpID EmpName ---------- ------------- 1 Mary 1 John 1 Sam 2 Alaina 2 Edward
结果我需要这种格式:
EmpID EmpName ---------- ------------- 1 Mary, John, Sam 2 Alaina, Edward
问:这个logging在同一个Employee
表中。 我几乎没有使用UDF,存储过程的经验,我需要通过查询来完成这件事情。如果没有使用UDF,SP就可能这样做。
- FOR XML PATH 技巧和文章
- 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 } }
@ OlegDok所选的答案可能会返回正确的结果。 但performance可能会很糟糕。 这个testing场景将会说明这一点。
创build临时表:
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 NN)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
在这种情况下,区别不是清理行的正确方法。 为了避免这种笛卡尔连接,像这样减less连接之前的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秒钟
我认为MSSQL中没有GROUP_CONCAT
函数。 本文展示了concactenating行值的不同方式。
当项目数量很小并且事先知道时,连接值
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 ;
更多的方式在这个环节。
这是开始时给出的例子的解决scheme:
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