Написание запроса наследования, написанного на SQL с использованием внутреннего соединения? - PullRequest
2 голосов
/ 11 октября 2010

Признаюсь, что мои знания SQL (с использованием mySQL) немного выходят за рамки стандартных запросов, необходимых для управления базами данных, и что большинство моих манипуляций с данными было сделано с помощью php.

Я очень хотелизмените это, и до сих пор пользовались успехом, и был бы признателен, если бы кто-нибудь посоветовал мне, как создать хранимую процедуру для запроса наследования на стандартной таблице, содержащей поля 'parent', 'child', внутреннее соединение с таблицей разрешений..

В качестве примера данных (для демонстрационных целей):

table_group_inherit

parent        child
------------------------
admin         moderator
member        guest
super_admin   admin
moderator     member

table_group_permissions

moderator    move_post
----------------------
super_admin  create_forum
admin        move_forum
guest        view_post
member       create_post
member       edit_post

Затем я вызову процедуру дляимя группы (например, «admin») для возврата массива ее разрешений («move_forum», «view_post», «create_post», edit_post »).Я не знаю, требуется ли мне итерация или рекурсия здесь (я читал что-то о mySQL, не поддерживающем это?), Но любые советы приветствуются.

(Примечание: я решил не использовать разрешения в качестве полей с TRUE / FALSEотметьте, как я собираюсь прикрепить дополнительные поля к каждому разрешению, например описание).

Ответы [ 4 ]

1 голос
/ 13 октября 2010

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

Если вы реализуете произвольный ориентированный граф (например, у вас есть профиль «автор», который может публиковать статьи, но не модерирует комментарии, и профиль «модератор», который может модерировать комментарии, но не публиковать статьи), вы можете хочу искать другое решение.

Одна из возможностей - отказаться от наследования и вручную установить разрешения для каждой группы.

Другая возможность - использовать таблицу наследования для хранения как прямого, так и косвенного наследования (то есть узел будет связан со всеми своими дочерними элементами, использующими «прямые» отношения, а также со всеми его потомками, использующими «косвенные» отношения. ). Эта стратегия требует от вас повторного создания всех косвенных связей в таблице всякий раз, когда вы изменяете одно из прямых отношений (это может быть сделано с помощью простого INSERT SELECT), но имеет преимущество в том, что требуется только одно соединение для доступа ко всем потомки.

Основная идея:

CREATE TABLE group_inherit (
  parent INT NOT NULL, 
  child INT NOT NULL, 
  distance INT NOT NULL, 
  PRIMARY KEY (parent,child)
);

/* Clean up indirect relations */
DELETE FROM group_inherit WHERE distance <> 0;

/* Repeat this for each D > 0 until the maximum distance is reached */
INSERT IGNORE INTO (parent, child, distance) 
SELECT fst.parent, snd.child, D
FROM group_inherit fst
INNER JOIN group_inherit snd ON snd.parent = fst.child
WHERE fst.distance = 0 AND snd.distance = D - 1;

/* Select all permissions for a user type */
SELECT perm.*
FROM group_permissions perm
INNER JOIN group_inherit ON perm.moderator = child
WHERE parent = ?

Цикл на расстоянии должен выполняться до тех пор, пока не будет доступно больше элементов расстояния D-1, что можно сделать с помощью запроса выбора или, если у вас есть, метаинформации о том, сколько строк было вставлено.

0 голосов
/ 14 октября 2010

Если иерархия просто означает «выше» = «больше разрешено», было бы гораздо проще просто дать рейтинг роли, чем выше, тем сильнее.А затем оцените действия.Любой пользователь в группе с рейтингом, равным или превышающим действие, может выполнить действие.

0 голосов
/ 11 октября 2010
У

был быстрый удар, поэтому рекомендуем вам дважды проверить результаты!

полный сценарий здесь: http://pastie.org/1213230

-- TABLES

drop table if exists roles;
create table roles
(
role_id tinyint unsigned not null primary key,
name varchar(255) unique not null,
parent_role_id tinyint unsigned,
key (parent_role_id)
)
engine=innodb;


drop table if exists actions;
create table actions
(
action_id smallint unsigned not null auto_increment primary key,
name varchar(255) unique not null
)
engine=innodb;


drop table if exists role_actions;
create table role_actions
(
role_id tinyint unsigned not null,
action_id smallint unsigned not null,
primary key (role_id, action_id)
)
engine=innodb;

-- STORED PROCEDURES

drop procedure if exists list_role_actions;

delimiter #

create procedure list_role_actions
(
in p_role_id tinyint unsigned
)
proc_main:begin

declare done tinyint unsigned default 0;
declare dpth smallint unsigned default 0;

drop temporary table if exists hier;
drop temporary table if exists tmp;

create temporary table hier(
 parent_role_id tinyint unsigned, 
 role_id tinyint unsigned, 
 depth smallint unsigned default 0
)engine = memory;

insert into hier select parent_role_id, role_id, dpth from roles where role_id = p_role_id;

/* http://dev.mysql.com/doc/refman/5.0/en/temporary-table-problems.html */

create temporary table tmp engine=memory select * from hier;

while done <> 1 do

    if exists( select 1 from roles r inner join hier on r.parent_role_id = hier.role_id and hier.depth = dpth) then

        insert into hier 
            select r.parent_role_id, r.role_id, dpth + 1 from roles r
            inner join tmp on r.parent_role_id = tmp.role_id and tmp.depth = dpth;

        set dpth = dpth + 1;            

        truncate table tmp;
        insert into tmp select * from hier where depth = dpth;

    else
        set done = 1;
    end if;

end while;

/*
select 
 r.*,
 p.name as parent_role_name,
 hier.depth
from 
 hier
inner join roles r on hier.role_id = r.role_id
inner join roles p on hier.parent_role_id = p.role_id
order by
 hier.depth, hier.role_id; 
*/

select
 ra.*,
 r.name as role_name,
 a.name as action_name
from
 role_actions ra
inner join hier h on h.role_id = ra.role_id
inner join actions a on ra.action_id = a.action_id
inner join roles r on ra.role_id = r.role_id
order by
 ra.role_id desc;

drop temporary table if exists hier;
drop temporary table if exists tmp;

end proc_main #


delimiter ;


-- TEST DATA

insert into roles values 
(0,'guest',1),(1,'member',2),(2,'moderator',3),(3,'admin',99),(99,'super admin',null);

insert into actions (name) values 
('view post'), ('create post'), ('edit post'), ('move forum'), ('create forum');

insert into role_actions values 
-- guest
(0,1),
-- member
(1,2),
-- moderator
(2,3),
-- admin
(3,4),
-- super admin
(99,5);

-- TESTING

call list_role_actions(0);
call list_role_actions(1);
call list_role_actions(2);
call list_role_actions(3);
call list_role_actions(99);
0 голосов
/ 11 октября 2010

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

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