我的一个表中有一个 User_Role 列,其中包含管道分隔值,如
guest|user_Admin|user_guest
user_Admin|guest
guest|user_guest|user_Admin
user_guest|user_Admin
user_Admin
我必须从上面的列中获取角色,其中包含 Admin 。 我不想要整个有Admin的行。
例如,如果列数据是 guest | user_guest | user_Admin ,则查询应仅返回 user_Admin 而不是整个值。
我怎样才能实现它?
答案 0 :(得分:1)
首先,我要告诉你你应该做什么......规范你的桌子。
如果相关表是用户角色表,则将user_role列拆分为单个值。如果它是包含其他列的较大表的一部分,那么您应该使用单独的表来存储用户角色信息。我还建议为每个值分配不同的级别,这样你就不必解析字符串了。
例如,有一个user_roles表,如
CREATE TABLE userRoles (userID INT, userRole INT);
...然后有一个角色表,如:
CREATE TABLE Roles (userRole INT IDENTITY(1, 1), roleDescription VARCHAR(255), roleLevel INT);
INSERT Roles (roleDescription, roleLevel) VALUES ('guest', 1), ('user_guest', 2), ('user_Admin', 3);
...这样,您可以通过连接和查找级别3而不是解析字符串来查找管理员角色。如果你的表格很小,没什么大不了的,但是你的表格越大,解析字符串就越昂贵。通过适当的索引,这将是检索所需数据的最快方法,同时在必要时可以轻松更改数据(需要更改角色名称?没问题......在Roles表中编辑roleDescription)。 / p>
但如果不这样做,可以考虑采用一些解决方案......
declare @1 table (ID INT IDENTITY(1, 1), User_Role varchar(255));
insert @1 (User_Role) values ('guest|hello_Kitty_Admin|user_guest'),('user_Admin_zzz|guest'),('guest|user_guest|greatest_admin_of_all_time'),('user_guest|user_Admin2'),('user_Admin'),('regular_user');
with t as (
select id, user_role, left(User_Role, charindex('admin', User_Role) + isnull(nullif(charindex('|', substring(User_Role, charindex('admin', User_Role), len(User_Role))), 0) - 2, len(User_Role))) zzz
from @1)
select id, isnull(right(zzz, isnull(nullif(charindex('|', reverse(zzz)), 0) - 1, len(zzz))), user_role) adminrole
from t;
...以上所做的就是解析你的user_role列,找到admin所在的位置并获取该值的行括号之间的所有内容(如果一个不存在,它只是完整地返回user_role)< / p>
declare @2 table(ID INT IDENTITY(1, 1), User_Role varchar(255));
insert @2 (User_Role) values ('guest|hello_Kitty_Admin|user_guest'),('user_Admin_zzz|guest'),('guest|user_guest|greatest_admin_of_all_time'),('user_guest|user_Admin2'),('user_Admin'),('regular_user');
with cte as (
select id, user_role, substring(user_role, 1, isnull(nullif(charindex('|', user_role), 0) - 1, len(user_role))) roles, nullif(charindex('|', user_role), 0) cindex, 1 L
from @2
union all
select id, user_role, substring(user_role, cindex+1, isnull(nullif(charindex('|', user_role, cindex+1), 0) - 1 - cindex, len(user_role))), nullif(charindex('|', user_role, cindex+1), 0), L + 1
from cte
where cindex is not null)
select id, max(isnull(x.y, user_role)) adminrole
from cte t
outer apply (
select min(roles) over (partition by len(roles))
from cte
where id = t.id
and roles like '%admin%') x(y)
group by id, x.y
order by id;
...这与使用不同方法的另一个做同样的事情。
或者您可以使用类似于其他答案的光标,稍作修改。
但实际上,正常化!
答案 1 :(得分:-1)
这应该有效。它使用游标来处理每个角色。
---更新---
更新了光标处理以涵盖许多边缘情况。
DECLARE @roles varchar(1000), @ADMININDEX int, @STARTINDEX int, @ENDINDEX int, @TEMP varchar(1000)
DECLARE MY_CURSOR CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT roles
FROM test
OPEN MY_CURSOR
FETCH NEXT FROM MY_CURSOR INTO @roles
WHILE @@FETCH_STATUS = 0
BEGIN
--Do something with Id here
SET @ADMININDEX = CHARINDEX('Admin',@roles)
IF (@ADMININDEX > 0)
BEGIN
SET @ADMININDEX += 5
SET @TEMP = CHARINDEX('|', SUBSTRING(@roles, @ADMININDEX, LEN(@roles)))
IF @TEMP = 0
SET @ENDINDEX = LEN(@roles) + 1
ELSE
SET @ENDINDEX = @TEMP - 1 + @ADMININDEX
SET @roles = SUBSTRING(@roles, 0, @ENDINDEX)
SET @STARTINDEX = LEN(@roles) - CHARINDEX('|', REVERSE(@roles)) + 2
IF (@STARTINDEX>@ADMININDEX)
PRINT @roles
ELSE
PRINT SUBSTRING(@roles, @STARTINDEX, @ENDINDEX)
END
FETCH NEXT FROM MY_CURSOR INTO @roles
END
CLOSE MY_CURSOR
DEALLOCATE MY_CURSOR