Reputation: 649
I'm using for xml path
to aggregate values:
select max(x.Ids), max(x.Number), (select country,city for json path) as [Json]
from t
outer apply (
select Stuff((select ',' + Convert(varchar(10),t2.Id)
from t t2
where t2.city=t.city and t2.country=t.country
for xml path(''),type).value('(./text())[1]','varchar(10)'),1,1,'') as Ids,
Stuff((select ',' + Convert(varchar(10),t2.Number)
from t t2
where t2.city=t.city and t2.country=t.country
for xml path(''),type).value('(./text())[1]','varchar(10)'),1,1,'') as Numbers
)x
The select-cases/queries inside of the outer apply
are identical. I wonder if it's possible to reuse this query? I tried creating a CTE inside of the outer apply
but this doesn't seem to be accepted by SQL Server:
select max(x.Ids), max(x.Number), (select country,city for json path) as [Json]
from t
outer apply (
with Test_CTE( Id, Number) AS (
SELECT ID, Number FROM t t2
where t2.city=t.city and t2.country=t.country
)
select Stuff((select ',' + Convert(varchar(10),t2.Id)
from Test_CTE t2
for xml path(''),type).value('(./text())[1]','varchar(10)'),1,1,'') as Ids,
Stuff((select ',' + Convert(varchar(10),t2.Number)
from Test_CTE t2
for xml path(''),type).value('(./text())[1]','varchar(10)'),1,1,'') as Numbers
)x
The above attempt gives the following error:
Incorrect syntax near the keyword 'with'.
Upvotes: 0
Views: 991
Reputation: 5753
In your for xml
, you can include both columns, avoid the concatenation with ',', and keep the columns named so that the output has the values wrapped in elements.
Then, in the outer logic, you can query for the specific element in question, convert to string, replace the close tags with blank strings, replace the open tags with a comma, and kill the first comma with stuff
.
Also you can consider applying a distinct
clause against your source before beginning to save some processing.
So, for data like this:
declare @t table (Id int, Number int, country char(1), city char(1));
insert @t values
(1, 101, 'a', 'z'),
(2, 102, 'b', 'y'),
(3, 103, 'a', 'z');
You can do this:
select [Json] = (select t.country, t.city for json path),
Ids = stuff(replace(replace(convert(varchar(max),
ap._xml.query('./Id')
),'<Id>', ','),'</Id>',''), 1, 1, ''),
Nums = stuff(replace(replace(convert(varchar(max),
ap._xml.query('./Number')
),'<Number>', ','),'</Number>',''), 1, 1, '')
from (select distinct t.city, t.country from @t t) t
cross apply (select _xml = (
select t2.Id,
t2.Number
from @t t2
where t2.city = t.city and t2.country = t.country
for xml path(''), type
)) ap;
And get output like this:
Json | Ids | Nums |
---|---|---|
[{"country":"b","city":"y"}] | 2 | 102 |
[{"country":"a","city":"z"}] | 1,3 | 101,103 |
But I don't know if you think the new approach is any more elegant than the original one. I'd say it is, but still pretty ugly. Keep in mind that newer versions of sql server have the string_agg
function that does this all for you.
Upvotes: 2