7

I am working on creating a view in SQL server one of the columns for which needs to be a comma separated value from a different table. Consider the tables below for instance -

CREATE TABLE Persons
(
    Id INT NOT NULL PRIMARY KEY,
    Name VARCHAR (100)
)

CREATE TABLE Skills
(
    Id INT NOT NULL PRIMARY KEY,
    Name VARCHAR (100),
)

CREATE TABLE PersonSkillLinks
(
    Id INT NOT NULL PRIMARY KEY,
    SkillId INT FOREIGN KEY REFERENCES Skills(Id),
    PersonId INT FOREIGN KEY REFERENCES Persons(Id),
)

Sample data

INSERT INTO Persons VALUES
(1, 'Peter'),
(2, 'Sam'),
(3, 'Chris')


INSERT INTO Skills VALUES
(1, 'Poetry'),
(2, 'Cooking'),
(3, 'Movies')

INSERT INTO PersonSkillLinks VALUES
(1, 1, 1),
(2, 2, 1),
(3, 3, 1)

What I want is something like shown in the image

enter image description here

While I have been able to get the results using the script below, I have a feeling that this is not the best (and certainly not the only) way to do as far as performance goes -

CREATE VIEW vwPersonsAndTheirSkills
AS
    SELECT p.Name,
    ISNULL(STUFF((SELECT ', ' + s.Name FROM Skills s JOIN PersonSkillLinks psl ON s.Id = psl.SkillId WHERE psl.personId = p.Id FOR XML PATH ('')), 1, 2, ''), '') AS Skill
    FROM Persons p 
GO

I also tried my luck with the script below -

CREATE VIEW vwPersonsAndTheirSkills
AS
 SELECT p.Name,
 ISNULL(STUFF((SELECT ', ' + skill.Name FOR XML PATH ('')), 1, 2, ''), '') AS Skill
 FROM persons p
 LEFT JOIN 
 (
    SELECT s.Name, psl.personid FROM Skills s
    JOIN PersonSkillLinks psl ON s.Id = psl.SkillId
 ) skill ON skill.personId = p.Id

GO

but it is not concatenating the strings and returning separate rows for each skill as shown below -

enter image description here

So, is my assumption about the first script correct? If so, what concept am I missing about it and what should be the most efficient way to achieve it.

6
  • 2
    What SQL Server version you are using ? Commented Mar 26, 2019 at 14:44
  • 2
    Which version of SQL Server? From SQL Server 2017 onwards, you may be better off using STRING_AGG()? (Also, if this is only for display purposes, it's normally advised that such processing is done in your presentation layer, out side of sql. Closely coupling your data layer and your presentation layer creates merry havoc in the future. Such as later wanting to be able to pick an item from the list, but finding out that the list items themselves can contain commas. At that time you'll be longing for a normalised structure keeping the names separate and having a column with the ids.)
    – MatBailie
    Commented Mar 26, 2019 at 14:44
  • 1
    I am using SQL Server 2016...I have read about the string aggregation but unfortunately its from 2017 onwards..
    – gkb
    Commented Mar 26, 2019 at 14:50
  • 1
    Why do you think the first script isn't "the best"? That's normally how a delimited list was created in SQL Server (until SQL Server 2017).
    – Thom A
    Commented Mar 26, 2019 at 14:50
  • @Larnu - The doubt was never about how to generate the list, but where..Like Yogesh mentioned in his answer, I thought the function was being called multiple times and hence slowing the query down..
    – gkb
    Commented Mar 27, 2019 at 6:04

1 Answer 1

5

I would try with APPLY :

SELECT p.Name, STUFF(ss.skills, 1, 2, '') AS Skill
FROM Persons p OUTER APPLY
     (SELECT ', ' + s.Name 
      FROM Skills s JOIN 
           PersonSkillLinks psl 
           ON s.Id = psl.SkillId 
      WHERE psl.personId = p.Id 
      FOR XML PATH ('')
     ) ss(skills);

By this way, optimizer will call STUFF() once not for all rows returned by outer query.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.