我正在尝试将一个基于mysql的应用程序迁移到Microsoft SQL Server 2005(不是自愿的,但这就是生活)。

在最初的应用程序中,我们几乎使用了完全符合ANSI-SQL的语句,只有一个重要的例外——我们相当频繁地使用MySQL的group_concat函数。

顺便说一下,Group_concat是这样做的:给定一个表,比如员工姓名和项目……

SELECT empName, projID FROM project_members;

返回:

ANDY   |  A100
ANDY   |  B391
ANDY   |  X010
TOM    |  A100
TOM    |  A510

... 下面是使用group_concat得到的结果:

SELECT 
    empName, group_concat(projID SEPARATOR ' / ') 
FROM 
    project_members 
GROUP BY 
    empName;

返回:

ANDY   |  A100 / B391 / X010
TOM    |  A100 / A510

所以我想知道的是:是否有可能写,说,一个用户定义的函数在SQL Server模拟group_concat的功能?

我几乎没有使用udf,存储过程或类似的东西的经验,只是直接的SQL,所以请在解释太多的一边犯错:)


当前回答

现在可能已经太晚了,但这不是最简单的方法吗?

SELECT     empName, projIDs = replace
                          ((SELECT Surname AS [data()]
                              FROM project_members
                              WHERE  empName = a.empName
                              ORDER BY empName FOR xml path('')), ' ', REQUIRED SEPERATOR)
FROM         project_members a
WHERE     empName IS NOT NULL
GROUP BY empName

其他回答

使用下面的代码,您必须在部署之前在项目属性上设置PermissionLevel=External,并通过运行ALTER database database_name set TRUSTWORTHY on将数据库更改为信任外部代码(请确保在其他地方阅读有关安全风险和替代方案[如证书])。

using System;
using System.Collections.Generic;
using System.Data.SqlTypes;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using Microsoft.SqlServer.Server;

[Serializable]
[SqlUserDefinedAggregate(Format.UserDefined,
    MaxByteSize=8000,
    IsInvariantToDuplicates=true,
    IsInvariantToNulls=true,
    IsInvariantToOrder=true,
    IsNullIfEmpty=true)]
public struct CommaDelimit : IBinarySerialize
{
    [Serializable]
    private class StringList : List<string>
    { }

    private StringList List;

    public void Init()
    {
        this.List = new StringList();
    }

    public void Accumulate(SqlString value)
    {
        if (!value.IsNull)
            this.Add(value.Value);
    }

    private void Add(string value)
    {
        if (!this.List.Contains(value))
            this.List.Add(value);
    }

    public void Merge(CommaDelimit group)
    {
        foreach (string s in group.List)
        {
            this.Add(s);
        }
    }

    void IBinarySerialize.Read(BinaryReader reader)
    {
        IFormatter formatter = new BinaryFormatter();
        this.List = (StringList)formatter.Deserialize(reader.BaseStream);
    }

    public SqlString Terminate()
    {
        if (this.List.Count == 0)
            return SqlString.Null;

        const string Separator = ", ";

        this.List.Sort();
 
        return new SqlString(String.Join(Separator, this.List.ToArray()));
   }

   void IBinarySerialize.Write(BinaryWriter writer)
   {
        IFormatter formatter = new BinaryFormatter();
        formatter.Serialize(writer.BaseStream, this.List);
    }
}

我已经使用如下查询进行了测试:

SELECT 
 dbo.CommaDelimit(X.value) [delimited] 
FROM 
 (
  SELECT 'D' [value] 
  UNION ALL SELECT 'B' [value] 
  UNION ALL SELECT 'B' [value] -- intentional duplicate
  UNION ALL SELECT 'A' [value] 
  UNION ALL SELECT 'C' [value] 
 ) X 

得到:A, B, C, D

我可能有点晚了,但这个方法对我来说是有效的,比COALESCE方法更容易。

SELECT STUFF(
             (SELECT ',' + Column_Name 
              FROM Table_Name
              FOR XML PATH (''))
             , 1, 1, '')

2021

@AbdusSalamAzad的答案是正确的。

SELECT STRING_AGG(my_col, ',') AS my_result FROM my_tbl;

如果结果太大,可能会出现“STRING_AGG aggregation result exceeded the limit of 8000 bytes”错误。使用LOB类型来避免结果截断。”,可以通过更改查询来修复:

SELECT STRING_AGG(convert(varchar(max), my_col), ',') AS my_result FROM my_tbl;

关于J Hardiman的回答,怎么样:

SELECT empName, projIDs=
  REPLACE(
    REPLACE(
      (SELECT REPLACE(projID, ' ', '-somebody-puts-microsoft-out-of-his-misery-please-') AS [data()] FROM project_members WHERE empName=a.empName FOR XML PATH('')), 
      ' ', 
      ' / '), 
    '-somebody-puts-microsoft-out-of-his-misery-please-',
    ' ') 
  FROM project_members a WHERE empName IS NOT NULL GROUP BY empName

顺便问一下,“姓氏”的使用是一个拼写错误还是我没有理解一个概念?

无论如何,非常感谢大家,因为这节省了我很多时间:)

SQL Server 2017引入了一个新的聚合函数

STRING_AGG(表达式,分隔符)。

连接字符串表达式的值并放置分隔符 它们之间的值。分隔符不会添加在字符串的末尾。

连接的元素可以通过追加WITHIN GROUP (ORDER by some_expression)来排序。

对于版本2005-2016,我通常在接受的答案中使用XML方法。

然而,这在某些情况下可能会失败。例如,如果要连接的数据包含CHAR(29)

FOR XML不能序列化数据…因为它 包含一个在XML中不允许的字符(0x001D)。

可以处理所有字符的更健壮的方法是使用CLR聚合。但是,使用这种方法对连接的元素应用排序比较困难。

对变量赋值的方法是不保证的,应该避免在生产代码中使用。