2009-02-02 51 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) 

現在放哪兒我離開那個開瓶器...