Row concatenation with FOR XML, but with multiple columns?

前端 未结 3 1996
轻奢々
轻奢々 2020-12-31 20:20

I often use queries like:

SELECT *
FROM   ThisTable
       OUTER APPLY (SELECT (SELECT SomeField + \' \' AS [data()]
                            FROM   SomeT         


        
相关标签:
3条回答
  • 2020-12-31 21:04

    You could do something like this. Instead of immediately sending the XML value to a string, this query uses the TYPE keyword to return an xml type object which can then be queried. The three query functions search the xml object for all instances of the Somefield element and return a new xml object containing just those values. Then the value function strips out the xml tags surrounding the values and passes them into a varchar(max)

    SELECT  ThisTable.ID
           ,[A].query('/Somefield').value('/', 'varchar(max)') AS [SomeField_Combined]
           ,[A].query('/Somefield2').value('/', 'varchar(max)') AS [SomeField2_Combined]
           ,[A].query('/Somefield3').value('/', 'varchar(max)') AS [SomeField3_Combined]
    FROM    ThisTable
            OUTER APPLY (
                         SELECT (
                                 SELECT SomeField + ' ' AS [SomeField]
                                       ,SomeField2 + ' ' AS [SomeField2]
                                       ,SomeField3 + ' ' AS [SomeField3]
                                 FROM   SomeTable
                                 WHERE  SomeTable.ID = ThisTable.ID
                                FOR
                                 XML PATH('')
                                    ,TYPE
                                ) AS [A]
                        ) [A]
    
    0 讨论(0)
  • 2020-12-31 21:05

    This is the same answer as I gave here: https://dba.stackexchange.com/questions/125771/multiple-column-concatenation/

    The OP of that question referenced the answer given here. You can see below that sometimes the simplest answer can be the best. If SomeTable is multiple tables then I would go ahead and put it into a CTE to avoid having the same complex code multiple times.

    I ran a few tests using a little over 6 mil rows. With an index on the ID column.

    Here is what I came up with.

    Your initial query:

    SELECT * FROM (
        SELECT t.id,
                stuff([M].query('/name').value('/', 'varchar(max)'),1,1,'') AS [SomeField_Combined1],
                stuff([M].query('/car').value('/', 'varchar(max)'),1,1,'') AS [SomeField_Combined2]
        FROM dbo.test t
        OUTER APPLY(SELECT (
                        SELECT id, ','+name AS name
                        ,','+car AS car
                        FROM test WHERE test.id=t.id
                        FOR XML PATH('') ,type)
                     AS  M) 
                M ) S
    GROUP BY id, SomeField_Combined1, SomeField_Combined2 
    

    This one ran for ~23 minutes.

    I ran this version which is the version I first learned. In some ways it seems like it should take longer but it doesn't.

    SELECT test.id,
        STUFF((SELECT ', ' + name
                FROM   test ThisTable
                WHERE  test.id = ThisTable.id
                FOR XML PATH ('')),1,2,'') AS ConcatenatedSomeField,
        STUFF((SELECT ', ' + car
                FROM   test ThisTable
                WHERE  test.id = ThisTable.id
                FOR XML PATH ('')),1,2,'') AS ConcatenatedSomeField2
    FROM test 
    GROUP BY id
    

    This version ran in just over 2 minutes.

    0 讨论(0)
  • You can create a CLR User-Defined Aggregate Function that does the concatenation for you.

    Your code would then look like this instead.

    select S.ID,
           dbo.Concat(S.SomeField1), 
           dbo.Concat(S.SomeField2),
           dbo.Concat(S.SomeField3)
    from SomeTable as S
    group by S.ID
    
    0 讨论(0)
提交回复
热议问题