TSQL Comma Separation

前端 未结 7 1899
夕颜
夕颜 2020-12-10 20:12

I\'m writing an export function, where I need to export contacts to Excel, and I\'ve run into a technical snag - or perhaps a gap in my SQL skills is closer to the truth. ;)

相关标签:
7条回答
  • 2020-12-10 20:49

    Try this

    declare @Roles nvarchar(max)
    
    select @Roles = case when @Roles is null then '' else @Roles + ', ' end + Role.Name
    from  Role
    inner join ContactRole on Role.ID = ContactRole.RoleID
    where ContactRole.ContactID = @ContactID
    
    select @Roles 
    

    update:

    Above code covers functionality for a single contact. You can create a scalar function with parameter @ContactID and call the function from a

    Select Name, dbo.GetContactRoles(ID) From Contact
    
    0 讨论(0)
  • 2020-12-10 20:52

    SQL Query:

    SELECT Contact.Name as cName, Role.Name as rName FROM Contact 
    JOIN ContactRole ON (Contact.ID==ContactRole.ContactID)
    JOIN Role ON  ON (Role.ID==ContactRole.RoleID)
    

    Next proceed with application logic

    forloop:
        array[ cName ] .= rName.', ';
    endforloop;
    
    0 讨论(0)
  • 2020-12-10 20:52

    You can write a function which outputs the roles as comma separated string when you pass it contact id. Then call this function in your select statement :)

    For example if you want to fetch products that are ordered by a customer in a particular order you can use this code:

        create function FetchProducts(@orderid int) returns varchar(1000)
        as
        begin
        declare prods cursor for select ProductName from products where 
                productid in (select ProductId from [Order Details] 
                  Where OrderId = @orderid)
    
        open prods
    
        declare @products  varchar(1000)
        declare @cp varchar(500)
        Select @products = ''
        fetch prods into @cp
    
        while @@fetch_status = 0
        begin
            SET @products = @products + ',' + @cp
            fetch prods into @cp
        end
    
        close prods
        deallocate prods
    
        return substring(@products, 2, len(@products)-1)
        end
    

    now you can use the function as follows:

        select orderid, orderdate, dbo.FetchProducts(orderid) 
    from orders where customerid = 'BERGS'
    
    0 讨论(0)
  • 2020-12-10 20:58

    EDIT: Rewritten from table to scalar function based on devio's idea so if you like this post vote for his answer.

    If CLR integration is not an option, you can accomplish this with a scalar function:

    create function dbo.getRole(
        @ContactId int)
    returns varchar(8000)
    as
    begin
    declare @Roles varchar(8000)
    
    select 
        @Roles = case when @Roles is null then '' else @Roles + ', ' end + Role.Name
    from Role
    inner join ContactRole on Role.ID = ContactRole.RoleID
    where ContactRole.ContactID = @ContactID
    
    return @Roles
    

    You can then call this function to calculate the comma-separated list for each contact:

    SELECT c.id, c.name, dbo.getRole(ID) as Roles
    FROM Contact
    
    0 讨论(0)
  • 2020-12-10 20:59

    Just because you use SQL Server 2005 (and if you are lucky and have all XML settings properly set), here is your simple SQL query (pure SQL and no functions):

    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
    

    To test if it works for you, just execute the whole snippet below:

    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
    

    and you should get the following result:

    ID          Name         Address     Roles
    ----------- ------------ ----------- ------------------
    1           p1-no role   NULL        NULL
    2           p2-one role  NULL        teacher,
    3           p3-two roles NULL        teacher,student,
    
    0 讨论(0)
  • 2020-12-10 21:03

    You can use a CLR user-defined aggregate to get such results. The user-defined aggregate can be invoked like the user-defined ones (e.g. SUM or MAX) and it doesn't use a cursor.

    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
    }
    

    In this posts you'll find the code as well as my solution of debugging problems I encountered.

    I used this aggregate in production environment and it performed really well.

    0 讨论(0)
提交回复
热议问题