2

How can I concatenate a specific attribute of all elements together using SQL XQuery?

I have a table like this:

InvoiceId   Details
---------   -----------------------------------------------------------------
1001        <Stuff Id="101" p="3" q="5"/><Stuff Id="102" q="4"/><Stuff Id="103"/>
1002        <Stuff Id="201" /><Stuff Id="202" q="2"/>

and need this result =>

InvoiceId  IdDetails
---------   ---------------------
1001        101,102,103
1002        201,202
Riley Major
  • 1,904
  • 23
  • 36
ARZ
  • 2,461
  • 3
  • 34
  • 56

3 Answers3

2

Simplest way would be to use REPLACE.

SELECT
   InvoiceId,
   SUBSTRING(
        REPLACE(
          REPLACE(
            CAST(Details AS nvarchar(max))
                 , '<Stuff Id="', ',')
               , '"/>', '')
             ,2, 2000000000)
FROM
   MyTable
gbn
  • 422,506
  • 82
  • 585
  • 676
  • we can't using Replace method,because the Stuff Element may have some other attributes in some cases! then how can get same result using XQuery?! – ARZ May 29 '11 at 10:08
  • @ARZ: Do you know what the elements are called? Is it a finite list or do you want to take any old XML and parse it...? – gbn May 29 '11 at 10:11
  • really it's not a finite list. – ARZ May 29 '11 at 10:23
  • @ARZ: Ok, so you don't know what the XML looks like? Also, I'm not sure this is valid XML in SQL Server terms: no root element. Is it stored in XML type or nvarchar? – gbn May 29 '11 at 10:26
  • Finally, to extract tag names, you may be able to adopt this http://stackoverflow.com/questions/6115397/use-xquery-to-get-at-this-data.- YMMV – gbn May 29 '11 at 10:30
2
declare @T table(InvoiceId int, Details xml)
insert into @T
select 1001, '<Stuff Id="101" p="3" q="5"/><Stuff Id="102" q="4"/><Stuff Id="103"/>' union all
select 1002, '<Stuff Id="201"/><Stuff Id="202" q="2"/>'

select InvoiceId,
       stuff((select ','+t.n.value('@Id', 'varchar(10)')
              from Details.nodes('Stuff') as t(n) 
              for xml path('')), 1, 1, '') as IdDetails  
from @T

Only unique values for Id.

select InvoiceId,
       stuff((select ','+s.id
              from Details.nodes('Stuff') as t(n)
                cross apply (select t.n.value('@Id', 'varchar(10)')) as s(id) 
              group by s.id
              for xml path('')), 1, 1, '') as IdDetails  
from @T
Mikael Eriksson
  • 136,425
  • 22
  • 210
  • 281
0

Perhaps a nicer way is to use the query function in the xml processing instead of a subselect and stuff. Building on Mikael Eriksson's answer, I found the following from this question to be quicker and more concise/readble:

declare @T table(InvoiceId int, Details xml)
insert into @T
select 1001, '<Stuff Id="101" p="3" q="5"/><Stuff Id="102" q="4"/><Stuff Id="103"/>' union all
select 1002, '<Stuff Id="201"/><Stuff Id="202" q="2"/>'

select InvoiceId, 
    t.n.query('for $i in Stuff return concat($i/@Id, ",")').value('.', 'nvarchar(max)')
from @T
    cross apply Details.nodes('.') as t(n)

However, the resulting strings include delimiting spaces and a terminating comma.

Community
  • 1
  • 1
Retiarius
  • 334
  • 2
  • 12