使用内部联接编写用SQL编写的继承查询?

时间:2010-10-11 15:10:15

标签: sql mysql

我承认我对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不支持这个的东西?),但欢迎任何建议。

(N.B。我选择使用权限作为具有TRUE / FALSE检查的字段,因为我打算将其他字段附加到每个权限,例如说明)。

4 个答案:

答案 0 :(得分:1)

如果您正在查看树层次结构,那么嵌套集模型可以很好地工作,但涉及继承表结构的重大更改。

如果您正在实施任意有向图(例如,您有一个“作者”个人资料可以发布文章但不能发表适度评论,而“主持人”个人资料可以审核评论但不能发布文章)我想寻找其他解决方案。

一种可能性是放弃继承并手动设置每个组的权限。

另一种可能性是使用继承表来存储直接和间接继承(即,一个节点将使用“直接”关系与其所有子节点相关,以及使用“间接”关系的所有子节点)。每当您更改其中一个直接关系时,此策略都要求您重新创建表中的所有间接关系(这可以通过使用简单的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的元素可用,这可以使用选择查询完成,或者,如果有的话,可以使用有关插入了多少行的元信息。

答案 1 :(得分:0)

您需要按照文档here创建嵌套的左右键,并将其用于继承查询。

答案 2 :(得分:0)

快速刺了一下,所以建议你仔细检查结果!

完整的脚本: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);

答案 3 :(得分:0)

如果层次结构只是意味着更高的up =更多允许,那么为角色提供评级会更容易,更高,更强大。 然后给予行动评级。评级等于或高于操作的组中的任何用户都可以执行操作。