views:

173

answers:

2

I'm trying to use the 'For Xml Path' T-SQL to generate a comma separated list of values from a column. This seems to work great, but the problem is I would like to get a count of the items in the comma separated list. Here is an example of the code I am using to generate the comma separated list:

Create Table #List ([col] varchar)

Insert Into #List Select '1';
Insert Into #List Select '2';
Insert Into #List Select '3'

Select ',' + [col] From #List For Xml Path('')

This gives the results 1,2,3 as expected, but there is no way to get the count that there are 3 items. Any attempt to add a count will just add it to the xml. I combined this code with a cte to get the count:

With CTE As (
    Select 
        [col] 
    From 
        #List
)
Select
   (Select ',' + [col] From #List For Xml Path('')) As [List],
   Count(*) As [Count]
From
   CTE

Is there an easier/cleaner way to get the count of nodes without using a CTE? It was pointed out that you can just duplicate the from clause inside the inner select and outside, but that requires keeping the from clauses in sync. I want to get both the list and count, but only have the from clause written once.

+1  A: 

You don't need the CTE you can use the subquery approach directly

SELECT 
      COUNT(*) AS [Count], 
      (SELECT ',' + [col] FROM #List FOR XML PATH('')) AS [List]
FROM #List
Martin Smith
This is an acceptable solution, but the problem is it requires duplicating the from logic twice. If I was pulling from a statement that had 10 joins I would have to repeat the joins inside the subselect and in the from clause. I want to be able to have the join logic written once and get the comma list and the count. I will modify my question. I tried to vote you up, but I don't have enough rep.
+2  A: 

How about drawing data from the CTE instead of the temp table?

With CTE As (
    Select 
        [col] 
    From 
        #List
    -- Many joins
    -- Complicated where clause
)
Select
   (Select ',' + [col] From Cte For Xml Path('')) As [List],
   Count(*) As [Count]
From
   CTE

This will allow you to keep your joins and search predicates in one place.

8kb
This is actually what I ended up using. However, I ended up having to put the complicated select into a temp table prior to using the CTE. When I put my join in the CTE directly the performance was terrible. It took about 2 seconds for the query inside the CTE to return, however, it would take the entire query with the comma list rollup hours. It took so long I never let it finish all the way. If I put the complex select into a temp table first the query would take a second to run. The only thing I can figure is that the select inside the CTE was being rerun for every iteration???