我一直无法找到解决困境的工作方案。
我正在使用SQL Server 2016,并在数据库中显示如下所示的2个表。
Users
表:
Id Name
----------
1 Lisa
2 Paul
3 John
4 Mike
5 Tom
Role
表:
Id UserId Role
------------------------
1 3 Manager
2 2,4,5 Developer
3 1 Designer
我正在寻找循环遍历Role
表的T-SQL代码,提取UserId
并从Id
表中检索每个Users
的相关名称。< / p>
所以循环结果如下所示:
John
Paul,Mike,Tom
Lisa
答案 0 :(得分:4)
FOR SQL SERVER 2017
SELECT R1.Id,STRING_AGG(U.Name , ','),R1.Role
FROM Users U
INNER JOIN
(
SELECT R.Id,S.value AS UserId,R.Role
FROM Role R
CROSS APPLY STRING_SPLIT (UserID, ',') S
) R1
ON U.Id=R1.UserId
GROUP BY R1.ID,R1.Role
ORDER BY R1.ID;
或强>
FOR SQL SERVER 2016
WITH CTE AS
(
SELECT R2.ID,U.Name,R2.UserId,R2.Role
FROM Users U
INNER JOIN
(
SELECT R.Id,S.value AS UserId,R.Role
FROM Role R
CROSS APPLY STRING_SPLIT (UserID, ',') S
)R2
ON U.id=R2.UserId
)
SELECT DISTINCT R1.Id,
STUFF((
SELECT ',' + name
FROM CTE R3
WHERE R1.Role = R3.Role
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '') AS NAME
,R1.Role
FROM CTE AS R1;
或强>
旧版本
With CTE AS
(
SELECT r.id,
u.name,
r.Role
FROM Users u
INNER JOIN Role r
ON ',' + CAST(r.Userid AS NVARCHAR(20)) + ',' like '%,' + CAST(u.id AS NVARCHAR(20)) + ',%'
)
SELECT DISTINCT id,
STUFF((
SELECT ',' + name
FROM CTE md
WHERE T.Role = md.Role
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '') AS NAME,
Role
FROM
CTE AS T
ORDER BY id
<强>输出强>
id NAME Role
1 John Manager
2 Paul,Mike,Tom Developer
3 Lisa Designer
<强>演示强>
答案 1 :(得分:3)
这里有几个问题。第一个问题是您以分隔格式存储值。接下来,因为您以分隔格式存储值,所以值将存储为varchar
。这也有问题,因为我会猜测表格Id
中的列Users
的值是int
;意味着需要进行隐式演员并破坏任何SARGability。
因此,解决方案是在我看来解决问题。因为你有很多关系,所以你需要一张额外的桌子。让我们现在就像你现在一样设计表格:
CREATE TABLE Users (Id int, Name varchar(100));
CREATE TABLE Role (Id int, UserId varchar(100), [Role] varchar(100));
INSERT INTO Users
VALUES (1,'Lisa'),
(2,'Paul'),
(3,'John'),
(4,'Mike'),
(5,'Tom');
INSERT INTO Roles
VALUES(1,'3','Manager'),
(2,'2,4,5','Developer'),
(3,'1','Designer');
现在,我们需要一个新表:
CREATE TABLE UserRoles (Id int, UserID int, RoleID int);
现在,我们可以将适当的行插入数据库。当您使用SQL Server 2016时,我们可以使用STRING_SPLIT
:
INSERT INTO UserRoles (UserID, RoleID)
SELECT SS.value, R.Id
FROM Roles R
CROSS APPLY STRING_SPLIT (UserID, ',') SS;
在此之后,如果您愿意,可以使用以下内容删除现有列,但是,我认为此刻不会有任何损害:
ALTER TABLE Roles DROP COLUMN UserID;
现在,我们可以正确查询数据:
SELECT *
FROM Users U
JOIN UserRoles UR ON U.ID = UR.UserID
JOIN Roles R ON UR.RoleID = R.Id;
如果您想要分隔这些数据,可以使用STUFF
,但不将其存储回来;我已经解释了如何纠正您的数据是有原因的! :)
SELECT [Role],
STUFF((SELECT ',' + [Name]
FROM Users U
JOIN UserRoles UR ON U.Id = UR.UserID
WHERE UR.RoleID = R.Id
FOR XML PATH ('')),1,1,'') AS Users
FROM Roles R;
如果您使用的是SQL Server 2017,则可以使用STRING_AGG
清理脚本:
DROP TABLE UserRoles;
DROP TABLE Users;
DROP TABLE Roles;
答案 2 :(得分:2)
试试这个解决方案:
declare @users table (Id int, Name varchar(100))
declare @role table (Id int, UserId varchar(100), [Role] varchar(100))
insert into @users values
(1, 'Lisa'),
(2, 'Paul'),
(3, 'John'),
(4, 'Mike'),
(5, 'Tom')
insert into @role values
(1, '3', 'Manager'),
(2, '2,4,5', 'Developer'),
(3, '1', 'Designer')
select * from @role [r]
join @users [u] on
CHARINDEX(',' + cast([u].Id as varchar(3)) + ',', ',' + [r].UserId + ',', 1) > 0
我根据Id
中的出现UserId
加入了两个表格。为了使其成为可能并避免匹配:2
与12
匹配,我决定仅匹配逗号包围的ID。这就是为什么我在查询中用逗号Id
包裹,并用逗号括起UserId
来匹配userId
的结尾和开头的ID。
此查询应该会给您带来满意的结果,但为了完全匹配您想要的输出,您必须将此查询包装在CTE中并使用字符串连接执行group by
:
;with cte as (
select [r].Id, [r].Role, [u].Name from @role [r]
join @users [u] on
CHARINDEX(',' + cast([u].Id as varchar(3)) + ',', ',' + [r].UserId + ',', 1) > 0
)
select Id,
(select Name + ',' from cte where Id = [c].Id for xml path('')) [Name],
--I believe this should work in your case, if so, just pick one column from these two
string_agg(Name + ',') [Name2],
Role
from cte [c]
group by Id, Role