Reputation: 1255
This might be a simple question for those who are experienced in working with JSON in SQL Server. I found this interesting way of aggregating strings using FOR XML
in here.
create table #t (id int, name varchar(20))
insert into #t
values (1, 'Matt'), (1, 'Rocks'), (2, 'Stylus')
select id
,Names = stuff((select ', ' + name as [text()]
from #t xt
where xt.id = t.id
for xml path('')), 1, 2, '')
from #t t
group by id
How can I do the same using JSON
instead of XML
?
Upvotes: 1
Views: 1333
Reputation: 67311
You cannot replace the XML approach with JSON. This string concatenation works due to some XML inner peculiarities, which are not the same in JSON.
Starting with SQL Server 2017 onwards you can use STRING_AGG()
, but with earlier versions, the XML approach is the way to go.
First the hint: The code you showed is not safe for the XML special characters. Check my example below.
First I declare a simple XML
DECLARE @xml XML=
N'<a>
<b>1</b>
<b>2</b>
<b>3</b>
<c>
<d>x</d>
<d>y</d>
<d>z</d>
</c>
</a>';
--The XPath .
tells the XML engine to use the current node (and all within)
--Therefore this will return any content within the XML
SELECT @xml.value('.','varchar(100)')
--You can specify the path to get 123 or xyz
SELECT @xml.query('/a/b').value('.','varchar(100)')
SELECT @xml.query('//d').value('.','varchar(100)')
Now your issue to concatenate tabular data:
DECLARE @tbl TABLE(SomeString VARCHAR(100));
INSERT INTO @tbl VALUES('This'),('will'),('concatenate'),('magically'),('Forbidden Characters & > <');
--The simple FOR XML
query will tag the column with <SomeString>
and each row with <row>
:
SELECT SomeString FROM @tbl FOR XML PATH('row');
--But we can create the same without any tags:
--Attention: Look closely, that the result - even without tags - is XML typed and looks like a hyper link in SSMS.
SELECT SomeString AS [*] FROM @tbl FOR XML PATH('');
--Now we can use as a sub-select within a surrounding query.
--The result is returned as string, not XML typed anymore... Look at the forbidden chars!
SELECT
(SELECT SomeString FROM @tbl FOR XML PATH('row'))
,(SELECT SomeString AS [*] FROM @tbl FOR XML PATH(''))
--We can use ,TYPE
to enforce the sub-select to be treated as XML typed itself
--This allows to use .query()
and/or .value()
SELECT
(SELECT SomeString FROM @tbl FOR XML PATH('row'),TYPE).query('data(//SomeString)').value('.','nvarchar(max)')
,(SELECT SomeString AS [*] FROM @tbl FOR XML PATH(''),TYPE).value('.','nvarchar(max)')
XQuery's .data()
can be used to concatenate named elements with blanks in between.
XQuery's .value()
must be used to re-escpae forbidden characters.
Upvotes: 4