Использование SQL для поиска набора в отношении «один ко многим» - PullRequest
2 голосов
/ 02 февраля 2009

Учитывая таблицы:

роль: роль, имя
разрешение :missionid, имя
role_permission: роль, идентификатор разрешения

У меня есть набор разрешений, и я хочу посмотреть, существует ли существующая роль с такими разрешениями или мне нужно создать новую. Обратите внимание, что я уже знаю идентификатор разрешения, поэтому таблицу разрешений можно игнорировать - я просто включил ее здесь для ясности.

Возможно ли это сделать в запросе SQL? Я полагаю, что это должен быть динамически генерируемый запрос.

Если нет, то есть ли лучший способ, чем метод грубой силы, просто перебирать каждую роль и проверять, имеет ли она точные разрешения?

Обратите внимание, я ищу роль с точным набором разрешений - ни больше, ни меньше.

Ответы [ 5 ]

4 голосов
/ 02 февраля 2009

Вы можете выбрать все роли, которые имеют поднабор разрешений, которые вы ищете. Подсчитайте количество разрешений и посмотрите, точно ли оно соответствует количеству необходимых вам разрешений:

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
2 голосов
/ 04 февраля 2009

Сделав хэш моего первого ответа на этот вопрос, вот немного оставленный вариант поля, который работает, но включает добавление данных в базу данных.

Хитрость заключается в том, чтобы добавить в таблицу разрешений столбец, содержащий уникальное значение для каждой строки.

Это довольно распространенная модель, которая даст точные результаты. Недостатком является то, что вы должны программировать, скрывая числовые эквиваленты.

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)

Теперь, где я оставил этот штопор ...

1 голос
/ 02 февраля 2009

В основном вам нужно проверить, есть ли роль, у которой есть точное количество отдельных разрешений, которые вы проверяете.

Я проверил эту хранимую процедуру на SQL Server 2005, и она возвращает только те идентификаторы ролей, которые имеют точное совпадение идентификаторов разрешений с теми, которые указаны в списке идентификаторов разрешений, разделенных запятыми -

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
1 голос
/ 02 февраля 2009

Это старый трюк 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)
)

Также не подтверждено. Красное вино всегда звучит хорошо.

0 голосов
/ 02 февраля 2009

Может быть, использовать подзапрос по линии ...

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

Извините, я не подтвердил это, но, потратив час на отладку PHP-кода для другого вопроса, я чувствую потребность в бокале красного вина.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...