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
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 -
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.
We can use AS aliases inside LEFT JOIN to make our snippet short and clean. For example, SELECT C.cat_name, P.prod_title FROM Categories1 AS C LEFT JOIN Products AS P ON C.cat_id= P.cat_id; Here, the SQL command selects common rows between Category and Products table. We can also use LEFT OUTER JOIN instead of LEFT JOIN.
We use the "Customers" and "Orders" tables, and give them the table aliases of "c" and "o" respectively (Here we use aliases to make the SQL shorter): Aliases can be useful when: When displaying the Customers table, make an ALIAS of the PostalCode column, the column should be called Pno instead.
The LEFT JOINis a clause of the SELECT statement. The LEFT JOINclause allows you to query datafrom multiple tables. The LEFT JOINreturns all rows from the left table and the matching rows from the right table. If no matching rows are found in the right table, NULL are used.
The SQL LEFT JOIN joins two tables based on a common column, and selects records that have matching values in these columns and remaining rows from the left table. SELECT Customers.customer_id, Customers.first_name, Orders.amount FROM Customers LEFT JOIN Orders ON Customers.customer_id = Orders.customer;
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With