Reputation: 75
I have a database table (table A) looks like this, in SQL Server 2016:
TableA:
TaskID - TaskName - AssignedTo
-------------------------------
1 Task 1 1,4
2 Task 2 3
3 Task 3 2,3
4 Task 4 2,4,5
I also have a TableB which is a lookup table for AssignedTo which looks like this:
TableB:
AssigneeID - Name
-------------------------------
1 John Smith
2 Janet Wright
3 Tom Morgan
4 Kevin Warren
5 Mike Taylor
I want to write a query to be able to genrate the following report/table:
TaskID - TaskName - NameAssignedTo
------------------------------------------------------------
1 Task 1 John Smith,Kevin Warren
2 Task 2 Tom Morgan
3 Task 3 Janet Wright,Tom Morgan
4 Task 4 Janet Wright,Kevin Warren,Mike Taylor
If it can be achieved by writing a SQL query that would be great. Anyone can help? Thank you very much!
Upvotes: 0
Views: 1073
Reputation: 415600
The schema you have is fundamentally broken. You really want an additional table here to express the TaskAssignments
like this:
TaskID | Assignee |
---|---|
1 | 1 |
1 | 4 |
2 | 3 |
3 | 2 |
3 | 3 |
4 | 2 |
4 | 4 |
4 | 5 |
You can find other solutions that will give you the results for this query, but this will make it much easier to write, it will perform multiple orders of magnitude better, and it's also better for answering questions (queries) you haven't even thought to ask yet.
General rule of thumb here: humans prefer fewer, wider tables, but computers do better with more, narrower tables. So we design the database storage to appease the computer, and write reports to show combined data for the humans.
Upvotes: 0
Reputation: 22157
Next time you would need to provide ##1-4. And learn from this answer what it means, i.e. a minimal reproducible example. You copy it to SSMS and launch it there.
Here is how to implement it in SQL Server 2016:
STRING_SPLIT()
to break it down, one AssignedTo per row.SELECT ... FOR XML ...
to revert it back to one row for each task.SQL
-- DDL and sample data population, start
DECLARE @tblA TABLE (TaskID INT PRIMARY KEY, TaskName VARCHAR(100), AssignedTo VARCHAR(30));
INSERT INTO @tblA (TaskID, TaskName, AssignedTo) VALUES
(1, 'Task 1', '1,4'),
(2, 'Task 2', '3'),
(3, 'Task 3', '2,3'),
(4, 'Task 4', '2,4,5');
DECLARE @tblB TABLE (AssigneeID INT PRIMARY KEY, [Name] VARCHAR(30));
INSERT INTO @tblB (AssigneeID, [Name]) VALUES
(1, 'John Smith'),
(2, 'Janet Wright'),
(3, 'Tom Morgan'),
(4, 'Kevin Warren'),
(5, 'Mike Taylor')
-- DDL and sample data population, end
DECLARE @separator CHAR(1) = ',';
;WITH cte AS
(
SELECT * FROM @tblA
CROSS APPLY (SELECT value FROM STRING_SPLIT(AssignedTo, @separator)) AS x
INNER JOIN @tblB AS b ON x.value = b.AssigneeID
)
SELECT p.TaskID, p.TaskName
, STUFF((SELECT DISTINCT
CONCAT(@separator, c.Name)
FROM cte AS c
WHERE c.TaskID = p.TaskID
FOR XML PATH ('')),
1, 1, '') AS NameAssignedTo
FROM cte AS p
GROUP BY p.TaskID, p.TaskName;
Output
+--------+----------+---------------------------------------+
| TaskID | TaskName | NameAssignedTo |
+--------+----------+---------------------------------------+
| 1 | Task 1 | John Smith,Kevin Warren |
| 2 | Task 2 | Tom Morgan |
| 3 | Task 3 | Janet Wright,Tom Morgan |
| 4 | Task 4 | Janet Wright,Kevin Warren,Mike Taylor |
+--------+----------+---------------------------------------+
Upvotes: 2