TSQL逗号分隔

Mar*_*s L 4 sql t-sql sql-server

我正在编写一个导出函数,我需要将联系人导出到Excel,而且我遇到了技术问题 - 或者我的SQL技能差距可能更接近事实了.;)

这是场景:我在数据库中有很多联系人.每个联系人都可以有许多不同的角色,例如联系人可以是C#Developer和DBA,也可以是DBA和IT-manager.这些分为三个表,如下所示:

-------------------   -------------------   -------------------
*     Contact     *   *   ContactRole   *   *      Role       *
-------------------   -------------------   -------------------
* ID              *   * ContactID       *   * ID              *
* Name            *   * RoleID          *   * Name            *
* Address         *   -------------------   -------------------
-------------------   
Run Code Online (Sandbox Code Playgroud)

不太难以遵循.有一组联系人和一组角色.这些由各个ID上的ContactRole表连接.

当我导出联系人时,我需要在导出中有一个列,所有角色都以逗号分隔,例如C# Developer, DBADBA, IT-manager.导出将从ASP.NET/C#代码隐藏完成,所以我想我可以在代码中执行此操作,但我觉得可以在SQL中执行此操作.

数据来自SQL Server 2005.

van*_*van 6

仅仅因为你使用SQL Server 2005(如果你很幸运并且正确设置了所有XML设置),这里是你的简单SQL查询(纯SQL而没有函数):

SELECT  c.ID, c.Name, c.Address, 
    (   SELECT      r.Name + ','
        FROM        "ContactRole" cr
        INNER JOIN  "Role" r
                ON  cr.RoleID = r.ID
        WHERE       cr.ContactID = c.ID
        ORDER BY    r.ID --r.Name
        FOR XML PATH('')
    ) AS "Roles"
FROM    "Contact" c
Run Code Online (Sandbox Code Playgroud)

要测试它是否适合您,只需执行以下整个代码段:

WITH "Contact" (ID, Name, Address) AS (
                SELECT 1, 'p1-no role', NULL
    UNION ALL   SELECT 2, 'p2-one role', NULL
    UNION ALL   SELECT 3, 'p3-two roles', NULL
)
, "Role" (ID, Name)AS (
                SELECT 1, 'teacher'
    UNION ALL   SELECT 2, 'student'
)
, "ContactRole" (ContactID, RoleID) AS (
                SELECT 2, 1
    UNION ALL   SELECT 3, 1
    UNION ALL   SELECT 3, 2
)

SELECT  c.ID, c.Name, c.Address, 
    (   SELECT      r.Name + ','
        FROM        "ContactRole" cr
        INNER JOIN  "Role" r
                ON  cr.RoleID = r.ID
        WHERE       cr.ContactID = c.ID
        ORDER BY    r.ID --r.Name
        FOR XML PATH('')
    ) AS "Roles"
FROM    "Contact" c
Run Code Online (Sandbox Code Playgroud)

你应该得到以下结果:

ID          Name         Address     Roles
----------- ------------ ----------- ------------------
1           p1-no role   NULL        NULL
2           p2-one role  NULL        teacher,
3           p3-two roles NULL        teacher,student,
Run Code Online (Sandbox Code Playgroud)


Mar*_*icz 5

您可以使用CLR用户定义的聚合来获得此类结果.可以像用户定义的聚合(例如SUM或MAX)一样调用用户定义的聚合,并且它不使用游标.

using System;
using System.Data;
using Microsoft.SqlServer.Server;
using System.Data.SqlTypes;
using System.IO;
using System.Text;

[Serializable()]
[SqlUserDefinedAggregate(
    Format.UserDefined,
    IsInvariantToNulls=true,
    IsInvariantToDuplicates=false,
    IsInvariantToOrder=false,
    MaxByteSize=8000)]
public class Concat : IBinarySerialize
{
    #region Private fields
    private string separator;
    private StringBuilder intermediateResult;
    #endregion

    #region IBinarySerialize members
    public void Read(BinaryReader r)
    {
        this.intermediateResult = new StringBuilder(r.ReadString());
    }

    public void Write(BinaryWriter w)
    {
        w.Write(this.intermediateResult.ToString());
    }
    #endregion

    #region Aggregation contract methods
    public void Init()
    {
        this.separator = ", ";
        this.intermediateResult = new StringBuilder();
    }

    public void Accumulate(SqlString pValue)
    {
        if (pValue.IsNull)
        {
            return;
        }

        if (this.intermediateResult.Length > 0)
        {
            this.intermediateResult.Append(this.separator);
        }
        this.intermediateResult.Append(pValue.Value);
    }

    public void Merge(Concat pOtherAggregate)
    {
        this.intermediateResult.Append(pOtherAggregate.intermediateResult);
    }

    public SqlString Terminate()
    {
        return this.intermediateResult.ToString();
    }
    #endregion
}
Run Code Online (Sandbox Code Playgroud)

这篇文章中,您将找到代码以及我遇到的调试问题的解决方案.

我在生产环境中使用了这个聚合,它表现得非常好.