在SQL中,如何将逗号分隔键字符串的列转换为逗号分隔值字符串
In SQL, how to convert a column of a comma separated key string to a comma separated value string
我有一个数据库 table (table A) 在 SQL Server 2016 中看起来像这样:
表A:
TaskID - TaskName - AssignedTo
-------------------------------
1 Task 1 1,4
2 Task 2 3
3 Task 3 2,3
4 Task 4 2,4,5
我还有一个 TableB,它是 AssignedTo 的查找 table,如下所示:
表B:
AssigneeID - Name
-------------------------------
1 John Smith
2 Janet Wright
3 Tom Morgan
4 Kevin Warren
5 Mike Taylor
我想编写一个查询以生成以下内容 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
如果可以通过编写 SQL 查询来实现,那就太好了。任何人都可以帮忙吗?非常感谢!
下次您需要提供##1-4。
并从这个答案中了解它的含义,即一个最小的可重现示例。
您将它复制到 SSMS 并在那里启动它。
以下是如何在 SQL Server 2016 中实现它:
STRING_SPLIT()
将其分解,每行一个 AssignedTo。
SELECT ... FOR XML ...
将每个任务恢复为一行。
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;
输出
+--------+----------+---------------------------------------+
| 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 |
+--------+----------+---------------------------------------+
您的架构基本上已损坏。你真的想要一个额外的table来表达TaskAssignments
这样的:
TaskID
Assignee
1
1
1
4
2
3
3
2
3
3
4
2
4
4
4
5
您可以找到其他解决方案来为您提供此查询的结果,但这会使它更容易编写,它的性能会好几个数量级,并且它也更适合回答您甚至没有想过要问的问题(查询)。
此处的一般经验法则:人类更喜欢更少、更宽的 tables,但 计算机 越多越好,更窄 tables。所以我们设计数据库存储来安抚计算机,并编写报告来为人类显示组合数据。
我有一个数据库 table (table A) 在 SQL Server 2016 中看起来像这样:
表A:
TaskID - TaskName - AssignedTo
-------------------------------
1 Task 1 1,4
2 Task 2 3
3 Task 3 2,3
4 Task 4 2,4,5
我还有一个 TableB,它是 AssignedTo 的查找 table,如下所示:
表B:
AssigneeID - Name
-------------------------------
1 John Smith
2 Janet Wright
3 Tom Morgan
4 Kevin Warren
5 Mike Taylor
我想编写一个查询以生成以下内容 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
如果可以通过编写 SQL 查询来实现,那就太好了。任何人都可以帮忙吗?非常感谢!
下次您需要提供##1-4。 并从这个答案中了解它的含义,即一个最小的可重现示例。 您将它复制到 SSMS 并在那里启动它。
以下是如何在 SQL Server 2016 中实现它:
STRING_SPLIT()
将其分解,每行一个 AssignedTo。SELECT ... FOR XML ...
将每个任务恢复为一行。
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;
输出
+--------+----------+---------------------------------------+
| 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 |
+--------+----------+---------------------------------------+
您的架构基本上已损坏。你真的想要一个额外的table来表达TaskAssignments
这样的:
TaskID | Assignee |
---|---|
1 | 1 |
1 | 4 |
2 | 3 |
3 | 2 |
3 | 3 |
4 | 2 |
4 | 4 |
4 | 5 |
您可以找到其他解决方案来为您提供此查询的结果,但这会使它更容易编写,它的性能会好几个数量级,并且它也更适合回答您甚至没有想过要问的问题(查询)。
此处的一般经验法则:人类更喜欢更少、更宽的 tables,但 计算机 越多越好,更窄 tables。所以我们设计数据库存储来安抚计算机,并编写报告来为人类显示组合数据。