Prior to SQL Server 2017, when string_agg
was finally introduced as a built in function,
the common method of concatenating strings from different rows was to use a combination of stuff
and for xml path('')
as demonstrated here.
Now, let's create and populate the sample tables (Please save us this step in your future questions):
CREATE TABLE AuthorMaster
(
id int,
Name char(3)
);
INSERT INTO AuthorMaster (ID, Name) VALUES
(1, 'ABC'),
(2, 'XYZ'),
(3, 'PQR');
CREATE TABLE Books
(
ID int,
BookName varchar(100),
AuthorIds varchar(100)
);
INSERT INTO Books (ID, BookName, AuthorIds) VALUES
(1, 'MATHEMATICS', '2,3'),
(2, 'Briar Queen', '1,3');
The problem with this design is the fact that instead of creating a BookToAuthor
bridge table to create the many-to-many relationship,
The database designer used a terrible idea and decided to store the author ids as a delimited string in the database. (Why is this such a terrible idea?)
To overcome this, we need to split this delimited string. Alas, prior to SQL Server 2016, when string_split
was finally introduced, you had to use a user defined function to split a delimited string.
Aaron Bertrand have published an article called Split strings the right way – or the next best way back in 2012 where he compares different string splitting functions.
I've used one of his examples from that article on this answer - the SplitStrings_XML
function:
CREATE FUNCTION dbo.SplitStrings_XML
(
@List NVARCHAR(MAX),
@Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
(
SELECT Item = y.i.value('(./text())[1]', 'nvarchar(4000)')
FROM
(
SELECT x = CONVERT(XML, '<i>'
+ REPLACE(@List, @Delimiter, '</i><i>')
+ '</i>').query('.')
) AS a CROSS APPLY x.nodes('i') AS y(i)
);
GO
So now that we have the sample data and the string splitting function, let's go to the query. Using the string aggregation query on the names of the authors, where the ids are in the AuthorIds column, and cross apply to bind that into the books table,
Here is the query I came up with:
SELECT ID,
BookName,
AuthorsNames
FROM Books
CROSS APPLY
(
SELECT STUFF(
(
SELECT ','+ Name
FROM AuthorMaster
WHERE Id IN
(
SELECT Item
FROM dbo.SplitStrings_XML(AuthorIds, ',')
)
FOR XML PATH('')
), 1, 1, '') As AuthorsNames
) As Names
And here are it's results:
ID BookName AuthorsNames
1 MATHEMATICS XYZ,PQR
2 Briar Queen ABC,PQR