2009-02-02 54 views
2

鉴于表:使用SQL搜索一组一到一对多的关系

角色:角色ID,名称
权限:permissionid,名
role_permission:角色ID,permissionid

我有一组权限,并且我想查看是否存在具有这些权限的现有角色,或者是否需要创建新权限。请注意,我已经知道了permissionid,所以权限表可以被忽略 - 为了清楚起见,我只是将它包括在内。

这是可能在SQL查询中做?我想它必须是一个动态生成的查询。

如果没有,是否有比仅仅遍历每个角色的蛮力方法更好的方法,并查看它是否具有确切的权限?

请注意,我正在寻找具有一组准确权限的角色 - 不多也不少。

回答

4

您可以选择具有您要查找的权限子集的所有角色。计数权限的数量,看看它是否恰好等于许可的数量,您需要:

select r.roleid 
from role r 
where not exists (select * from role_permissions rp where rp.roleid = r.roleid and rp.permissionid not in (1,2,3,4)) -- id of permissions 
    and (select count(*) from role_permissions rp where rp.roleid = r.roleid) = 4 -- number of permissions 
+0

这假设role_permission中没有重复,但这大概是该表的一个约束。 – 2009-02-02 19:31:20

+0

是的,我认为这是一个可以接受的假设。 – 2009-02-02 19:34:58

+0

如果只有一部分权限适用,这实际上也会选择角色 - 例如,如果您拥有权限为1,2,3,4的角色,并且您运行此查询,则当您尝试“1,2,3 ,4“或”1,2,3“。 – gregmac 2009-02-02 19:51:32

0

也许使用子查询沿线的...

SELECT * FROM role r 
WHERE r.rolid = (SELECT x.roledid 
       FROM role_permission 
       WHERE x.permissionid in (1,2,3,4); 

对不起,没有验证这,但花了一个小时调试PHP代码的另一个问题,我觉得需要一杯红酒。

1

这是一个老把戏SQL(工作在Oracle中,至少):

SELECT roleid FROM role_permission t1 
WHERE NOT EXISTS (
(SELECT permissionid FROM role_permission t2 WHERE t2.roleid = t1.roleid 
MINUS 
SELECT permissionid FROM role_permission WHERE roleid = 'Admin') 
UNION 
(SELECT permissionid FROM role_permission t2 WHERE roleid = 'Admin' 
MINUS 
SELECT permissionid FROM role_permsission t2 WHERE t2.roleid = t1.roleid) 
) 

也没有经过验证。红酒总是听起来不错。

1

您基本上需要检查是否有一个角色具有您检查的确切数量的不同权限。

我已经检查SQL Server 2005上该存储过程,并返回只有有权限ID的精确匹配到那些在传递逗号列表分隔许可IDS那些角色ID -

CREATE PROC get_roles_for_permissions (@list nvarchar(max)) -- @list is a comma separated list of your permission ids 
AS 
SET NOCOUNT ON 

BEGIN 

DECLARE  @index INT, @start_index INT, @id INT 
DECLARE  @permission_ids TABLE (id INT)   

    SELECT @index = 1 
    SELECT @start_index = 1 
    WHILE @index <= DATALENGTH(@list) 
    BEGIN 

     IF SUBSTRING(@list,@index,1) = ',' 
     BEGIN 
       SELECT @id = CAST(SUBSTRING(@list, @start_index, @index - @start_index) AS INT) 
       INSERT INTO @permission_ids ([id]) VALUES (@id) 
       SELECT @start_index = @index + 1 
     END 
     SELECT @index = @index + 1 
    END 
    SELECT @id = CAST(SUBSTRING(@list, @start_index, @index - @start_index) AS INT) 
    INSERT INTO @permission_ids ([id]) VALUES (@id) 

SELECT 
r.roleid 
FROM 
role r 
INNER JOIN 
role_permission rp 
ON r.roleid = rp.roleid 
INNER JOIN 
@permission_ids ids 
ON 
rp.permissionid = ids.id 
GROUP BY r.roleid 
HAVING(SELECT COUNT(*) 
     FROM role_permission 
     WHERE roleid = r.roleid) = (SELECT COUNT(*) FROM @permission_ids) 

END 

示例数据

CREATE TABLE [dbo].[role](
    [roleid] [int] IDENTITY(1,1) NOT NULL, 
    [name] [nvarchar](50) 
    ) 

CREATE TABLE [dbo].[permission](
    [permissionid] [int] IDENTITY(1,1) NOT NULL, 
    [name] [nvarchar](50) 
    ) 

CREATE TABLE [dbo].[role_permission](
    [roleid] [int], 
    [permissionid] [int] 
    ) 

INSERT INTO role(name) VALUES ('Role1') 
INSERT INTO role(name) VALUES ('Role2') 
INSERT INTO role(name) VALUES ('Role3') 
INSERT INTO role(name) VALUES ('Role4') 

INSERT INTO permission(name) VALUES ('Permission1') 
INSERT INTO permission(name) VALUES ('Permission2') 
INSERT INTO permission(name) VALUES ('Permission3') 
INSERT INTO permission(name) VALUES ('Permission4') 

INSERT INTO role_permission(roleid, permissionid) VALUES (1, 1) 
INSERT INTO role_permission(roleid, permissionid) VALUES (1, 2) 
INSERT INTO role_permission(roleid, permissionid) VALUES (1, 3) 
INSERT INTO role_permission(roleid, permissionid) VALUES (1, 4) 
INSERT INTO role_permission(roleid, permissionid) VALUES (2, 2) 
INSERT INTO role_permission(roleid, permissionid) VALUES (2, 3) 
INSERT INTO role_permission(roleid, permissionid) VALUES (2, 4) 
INSERT INTO role_permission(roleid, permissionid) VALUES (3, 3) 
INSERT INTO role_permission(roleid, permissionid) VALUES (3, 4) 
INSERT INTO role_permission(roleid, permissionid) VALUES (4, 4) 

EXEC get_roles_for_permissions '3,4' -- RETURNS roleid 3 
2

已经做了我的第一个答案的散列这个问题,这里有一个稍微左外野替代其工作,但不涉及将数据添加到数据库中。

诀窍是向权限表中添加一列,该权限表为每行保存一个唯一值。

这是一个相当普遍的模式,并会给出准确的结果。缺点是你必须编码隐藏数字等值。

id int(10) 
name varchar(45) 
value int(10) 

然后内容将变为:

Permission:   Role     Role_Permission 
id name value  id name    roleid permissionid 
-- ---- -----  -- ----    ------ ------------ 
1 Read  8   1 Admin    1   1 
2 Write 16   2 DataAdmin   1   2 
3 Update 32   3 User     1   3 
4 Delete 64         1   4 
               2   1 
               2   3 
               2   4 

然后角色的每个组合给出了一个独特的价值:

SELECT x.roleid, sum(value) FROM role_permission x 
inner join permission p 
on x.permissionid = p.id 
Group by x.roleid 

,并提供:

roleid sum(value) 
------ ---------- 
    1   120  (the sum of permissions 1+2+3+4 = 120) 
    2   104  (the sum of permissions 1+3+4 = 104) 

现在放哪儿我离开那个开瓶器...