4

I have a query that looks like this

SELECT J.JobID,T.Title FROM JobsTagMap J
Left Join Tags T
ON J.TagID=T.TagID

That returns the following dataset (simplified, JobID is actually a UniqueIdentifier)

JobID    Title
1        Tag1
1        Tag2
2        Tag2
2        Tag5
2        Tag9

Now, i'd like to group this by the JobID-column and concatenate the Title, so the results is as following

JobID    Title
1        Tag1,Tag2
2        Tag2,Tag5,Tag9

How would i do that?

Arion
  • 31,011
  • 10
  • 70
  • 88
Anton Gildebrand
  • 3,641
  • 12
  • 50
  • 86

3 Answers3

10

If you are using sql server 2005+. Then you can do like this:

SELECT 
    JobsTagMap.JobID,
    STUFF
    (
        (
            SELECT 
                ',' +Title
            FROM
                Tags
            WHERE
                Tags.TagID=JobsTagMap.TagID
            FOR XML PATH('')
        )
    ,1,1,'') AS Title
FROM JobsTagMap

EDIT

Because you did not show us the table structure and the data in the different tables. It was a lite bit hard to know. So I assume that your table structure looks something like this:

CREATE TABLE JobsTagMap
(
    JobID INT,
    TagID INT
)

CREATE TABLE Tags
(
    TagID INT,
    Title VARCHAR(100)
)

With this data:

INSERT INTO JobsTagMap
VALUES(1,1),(1,2),(2,2),(2,4),(2,5)

INSERT INTO Tags
VALUES(1,'Tag1'),(2,'Tag2'),(3,'Tag2'),(4,'Tag5'),(5,'Tag9')

If you are getting that data that you are showing the JobID cannot be unique. You might have the a Job table somewhere where it is unique. If you just want to use these table that you are showing then you need to do something like this:

;WITH CTE
AS
(
    SELECT
        ROW_NUMBER() OVER(PARTITION BY JobID ORDER BY JobID) AS RowNbr,
        JobsTagMap.*
    FROM
        JobsTagMap
)
SELECT
    *,
    STUFF
    (
        (
            SELECT 
                ',' +Title
            FROM
                Tags
                JOIN JobsTagMap
                    ON Tags.TagID=JobsTagMap.TagID
            WHERE
                JobsTagMap.JobID=CTE.JobID
            FOR XML PATH('')
        )
    ,1,1,'') AS Title
FROM
    CTE
WHERE
    CTE.RowNbr=1

This will get you this result:

1   1   1   Tag1,Tag2
1   2   2   Tag2,Tag5,Tag9

So in the future always show what table structure and it data. That will give you better answers

Arion
  • 31,011
  • 10
  • 70
  • 88
  • I tried a little with this, and added a Group by at last, "GROUP BY JobsTagMap.JobID", but then i get this error "Column 'JobsTagMap.TagID' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause." – Anton Gildebrand May 05 '12 at 15:59
1

I use a scalar function for exactly that. There are going to be some purist that decry should never use a row based operation but hey this works and if you are only returning a few rows then response time is fine.

CREATE FUNCTION [dbo].[JoinMVText]

(

  @sID int,

  @fieldID tinyint

)

RETURNS VARCHAR(MAX)

AS 

BEGIN

   DECLARE @MVtextList varchar(max)

   SELECT @MVtextList = COALESCE(@MVtextList + '; ', '') + docMVtext.value

   FROM docMVtext with (nolock) 

   WHERE docMVtext.sID = @sID and fieldID = @fieldID

   RETURN @MVtextList

END
paparazzo
  • 44,497
  • 23
  • 105
  • 176
-1

I had the same problem as you did and I figured out how to workaround slow sub-selects.

Using GROUP BY:

(70500 rows affected)

 SQL Server Execution Times:
   CPU time = 94 ms,  elapsed time = 833 ms.

Using Sub-Selects:

(70500 rows affected)

 SQL Server Execution Times:
   CPU time = 1469 ms,  elapsed time = 2323 ms.

Sub-selects are more then 4 times slower...

Here's the solution:

SELECT 
    J.JobID,
    STRING_AGG(ISNULL(T.Title, ''), ',') as Title
FROM JobsTagMap J
LEFT JOIN Tags T ON J.TagID = T.TagID
GROUP BY J.JobID;

Let me know if something is not clear enough :)