STRING_AGG giving different results than STUFF + FOR XML PATH

JasonW-5564 161 Reputation points

I will admit I am no expert in using STUFF + FOR XML PATH! I am trying to improve a query that gets executed multiple times and 48% of that execution time is in the FOR XML PATH part of the query. We are running SQL Server 2017 so I am trying to replace it with the new STRING_AGG function instead. Below are my two attempts at each. I am getting different results for each. I am trying to get the same results so I can just replace the FOR XML PATH code and hopefully see some performance gains. Hopefully this is easy to follow as I have pulled this out of a much larger query just to isolate these calls.


select distinct im.shortcode FROM issuer_metadata im FOR xml path('')
, type ).value('.', 'varchar(max)'),1,1,'')


select distinct string_agg(im.shortcode,'')
FROM issuer_metadata im

--Results: Note different results


SQL Server
SQL Server
A family of Microsoft relational database management and analysis systems for e-commerce, line-of-business, and data warehousing solutions.
13,139 questions
A Microsoft extension to the ANSI SQL language that includes procedural programming, local variables, and various support functions.
4,586 questions
{count} votes

Accepted answer
  1. Tom Phillips 17,721 Reputation points

    To get the same results, your command should be:
    select string_agg( shortcode,'')
    FROM (
    select distinct im.shortcode FROM issuer_metadata
    ) a

    There is no way to string_agg a distinct list currently.

    1 person found this answer helpful.

3 additional answers

Sort by: Most helpful
  1. Olaf Helper 42,581 Reputation points

    Of course you get a different result.
    In the XML solution you query first a distinct resultset of all "shortcode" and stuff then as result.
    In String_Agg you query all "shortcode", also dulicates, agg them and retrieve the distinct result, which is the same as without the distinct, because it's one result.

    You will get the same result using a subquery to get first the distinct list

    select string_agg(sub.shortcode,'')
        (select distinct im.shortcode
         FROM issuer_metadata im) as sub
    1 person found this answer helpful.
    0 comments No comments

  2. EchoLiu-MSFT 14,576 Reputation points

    Hi @JasonW-5564 ,

    Please try:

        STUFF((select distinct ' '+CAST(im.shortcode AS VARCHAR(30)) AS [text()]  
        FROM issuer_metadata im   
        FOR XML PATH('')), 1, 1, NULL) AS Abbr  

    If you have any question, please feel free to let me know.


    If the answer is helpful, please click "Accept Answer" and upvote it.

    1 person found this answer helpful.
    0 comments No comments

  3. Sean Williams 1 Reputation point

    string_agg does not work distinct.
    Adding the distinct before the string_agg is pointless as there is only one result.

    The only way I have been able to solve this for now is ( without doing a select as the join but essentially the same )

    WITH cte
    FROM issuer_metadata AS im)
    SELECT STRING_AGG(cte.shortcode, '')
    FROM cte;

    this has been a gripe for a while as to why it's not possible to do SELECT STRING_AGG( DISTINCT shortcode ) from issuer_metadata

    0 comments No comments