You can try this:
declare @x as xml ='<root>
<n1>hello world</n1>
<n2>A0001</n2>
<n2>A0002</n2>
<n2>A0003</n2>
<n2>A0004</n2>
</root>';
SELECT @x.value('(/root/n1/text())[1]','nvarchar(max)') AS n1
,@x.query('data(/root/n2)').value('.','nvarchar(max)');
It's a pitty, that data()
does not allow to specify the delimiter. It will always be a blank. But you can use REPLACE()
:
SELECT @x.value('(/root/n1/text())[1]','nvarchar(max)') AS n1
,REPLACE(@x.query('data(/root/n2)').value('.','nvarchar(max)'),' ',',');
The backdraw: If your values may include a blank, this will fail...
You can use XQuery instead:
SELECT @x.value('(/root/n1/text())[1]','nvarchar(max)') AS n1
,STUFF(
@x.query('for $n2 in /root/n2/text()
return <x>{concat(",",$n2)}</x>').value('.','nvarchar(max)'),1,1,'');
In this approach we use a FLWOR-query to run through <n2>
elements and create a new XML where the content is extended with a comma:
<x>,A0001</x>
<x>,A0002</x>
<x>,A0003</x>
<x>,A0004</x>
This we can read as one with the XQuery path '.'
. Removing the leading comma uses STUFF()
(as similar string aggregation approaches do). We could use XQuery sub-string()
too:
SELECT @x.value('(/root/n1/text())[1]','nvarchar(max)') AS n1
,@x.query('for $n2 in /root/n2/text()
return <x>{concat(",",$n2)}</x>')
.value('substring(.,2,1000)','nvarchar(max)')