使用MySQL从树中选择记录

时间:2011-01-18 10:50:51

标签: php mysql tree acl

注意:在阅读所有这些其他内容之前,您可能希望跳到底部阅读实际问题。

我正在为CakePHP开发ACL实现。主要是因为我试图将它与AuthComponeny分离,因此我可以将Authsome用于我的项目。我有实施理论,但我遇到了一些绊脚石。

显然,我希望将数据库查询的数量降至最低。所以我在这里问这是否有可能(我非常怀疑它是。)

假设这样的表结构:

id - int(10), auto_increment, primary_key, not null
parent_id - int(10), null
model - varchar(255), utf8_bin, null
foreign_key - int(10), null
alias - varchar(255), utf8_bin, null,
lft - int(10), null
rght - int(10), null

还有一些要测试的记录(控制器是根节点,我可能会得到lft和rght值错误):

1, null, null, null, controllers,          1,  14
2, 1,    null, null, one_test_controllers, 2,  7
3, 2,    null, null, one_action,           3,  4
4, 2,    null, null, two_action,           5,  6
5, 1,    null, null, two_test_controllers, 8,  13
6, 5,    null, null, one_action,           9,  10
7, 5,    null, null  two_action,           11, 12

两条测试路径:

$test1 = '/controllers/one_test_controller/two_action';
$test2 = '/controllers/two_test_controller/two_action';

给出这些结果,返回从最相关到​​最不相关的ID数组:

// Result 1
array(
    0 => 4,
    1 => 2,
    2 => 1
)

// Result 2
array(
    0 => 7,
    1 => 5,
    2 => 1
)

我目前正在做的是将路径分解为和数组,(在本例中使用$ test1)首先找到与别名“two_action”匹配的所有记录;然后遍历结果并查找与最后一个结果的父ID匹配的所有记录,并使用别名“one_test_controller”。然后重复,直到parent_id = 0。

它有效,但显然多个递归SQL查询并不理想,是否有一个神奇的SQL查询可以帮助我这个?或者我是否正确地认为这是最好的?

1 个答案:

答案 0 :(得分:2)

是吗?您已经获得了通过使用邻接树在单个传递中解析路径来获取数据的结构。

但是,如果不存储完整路径/需要唯一的节点名称,则无法从下往上搜索。考虑一下 - 在你的两个测试用例中,你都是从'two_action'开始,但是要寻找2个不同的叶子。如果您将整个路径存储在表中(或者可以通过查询中的id引用节点),那么......

SELECT ancestors.*
FROM ahier ancestors,
(SELECT lft, rght
  FROM ahier ref
  WHERE ref.path='/controllers/one_test_controller/two_action') ilv
WHERE (ancestors.lft >= ilv.left AND ancestors.rght <= ilv.rght)
ORDER BY ancestors.lft ASC;

或使用ID:

SELECT ancestors.*
FROM ahier ancestors,
(SELECT lft, rght
  FROM ahier ref
  WHERE ref.id=4) ilv
WHERE (ancestors.lft >= ilv.left AND ancestors.rght <= ilv.rght)
ORDER BY ancestors.lft ASC;

或者,您可以编写一个查询来返回具有特定节点别名的每个可能路径 - 但这也不会非常有效....

SELECT treenum, ancestors.*
FROM ahier ancestors,
(SELECT lft, rght, id as treenum
  FROM ahier ref
  WHERE ref.alias='two_action') ilv
WHERE (ancestors.lft >= ilv.left AND ancestors.rght <= ilv.rght)
ORDER BY treenum, ancestors.lft ASC;

(很容易从parent_id重建lft和rght)