views:

1958

answers:

7

If I have a table like this:

+------------+
| Id | Value |
+------------+
| 1  | 'A'   |
|------------|
| 1  | 'B'   |
|------------|
| 2  | 'C'   |
+------------+

How can I get a resultset like this:

+------------+
| Id | Value |
+------------+
| 1  | 'AB'  |
|------------|
| 2  | 'C'   |
+------------+

I know this is really easy to do in MySQL using GROUP_CONCAT, but I need to be able to do it in MSSQL 2005

Thanks

(Duplicate of http://stackoverflow.com/questions/273238/how-to-use-group-by-to-concatenate-strings-in-mssql)

A: 
SELECT Id, CONCAT(Value) As Value FROM TheTable GROUP BY Id
Tomas Lycken
Doesn't seem to work in SQL Server
Boog
+1  A: 

Often asked here.

The most efficient way is using the FOR XML PATH trick.

Cade Roux
+1  A: 

This will do:

SELECT mt.ID,
       SUBSTRING((SELECT mt2.Value
                  FROM   MyTable AS mt2
                  WHERE  mt2.ID = mt.ID
                  ORDER BY mt2.VALUE
                  FOR XML PATH('')), 3, 2000) AS JoinedValue
FROM   MyTable AS mt
Aaron Alton
This concats the values in a XML document, which is undesirable.
Boog
No, it uses XML functions. There are no XML documents involved. Why is it "undesirable"?
Aaron Alton
A: 

This just came to me as one possible solution. I have no idea as to performance, but I thought it would be an interesting way to solve the problem. I tested that it works in a simple situation (I didn't code to account for NULLs). Feel free to give it a test to see if it performs well for you.

The table that I used included an id (my_id). That could really be any column that is unique within the group (grp_id), so it could be a date column or whatever.

;WITH CTE AS (
    SELECT
     T1.my_id,
     T1.grp_id,
     CAST(T1.my_str AS VARCHAR) AS my_str
    FROM
     dbo.Test_Group_Concat T1
    WHERE NOT EXISTS (SELECT * FROM dbo.Test_Group_Concat T2 WHERE T2.grp_id = T1.grp_id AND T2.my_id < T1.my_id)
    UNION ALL
    SELECT
     T3.my_id,
     T3.grp_id,
     CAST(CTE.my_str + T3.my_str AS VARCHAR)
    FROM
     CTE
    INNER JOIN dbo.Test_Group_Concat T3 ON
     T3.grp_id = CTE.grp_id AND
     T3.my_id > CTE.my_id
    WHERE
     NOT EXISTS (SELECT * FROM dbo.Test_Group_Concat T4 WHERE
     T4.grp_id = CTE.grp_id AND
     T4.my_id > CTE.my_id AND
     T4.my_id < T3.my_id)
)
SELECT
    CTE.grp_id,
    CTE.my_str
FROM
    CTE
INNER JOIN (SELECT grp_id, MAX(my_id) AS my_id FROM CTE GROUP BY grp_id) SQ ON
    SQ.grp_id = CTE.grp_id AND
    SQ.my_id = CTE.my_id
ORDER BY
    CTE.grp_id
Tom H.
+2  A: 
SELECT Id
      (SELECT CAST(ProjectNumber AS VARCHAR(MAX)) 
         FROM Table
         WHERE (Id = Table1.Id)
         FOR XML PATH ('')
      ) AS Value
FROM Table as Table1

This should do the trick

Sem Dendoncker
+1  A: 

For a clean and efficient solution you can create an user defined aggregate function, there is even an example that does just what you need.
You can then use it like any other aggregate function (with a standard query plan): query plan

Pent Ploompuu