【问题标题】:SQL group_concat function in SQL Server [duplicate]SQL Server中的SQL group_concat函数[重复]
【发布时间】: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 版本?你看stackoverflow.com/questions/451415/…
  • 我正在使用 sql server 2008 @ BartekR
  • @OlegDok 因为我是 sql 新手,所以我已经尝试过该帖子,但我对其中的 column_names 感到困惑..:(

标签: sql-server-2008 asp-classic


【解决方案1】:
  1. 用于 XML 路径 trickarticle
  2. CLR 用户定义聚合
  3. 对于 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】:

我认为 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】:

    @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 秒

    【讨论】:

      【解决方案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
      

      【讨论】:

        猜你喜欢
        • 2012-02-15
        • 2013-10-07
        • 2020-03-09
        • 1970-01-01
        • 2013-05-13
        • 2013-06-15
        • 2011-07-17
        • 1970-01-01
        相关资源
        最近更新 更多