使用 MySQL 从树中选择记录

发布于 2024-10-12 16:51:25 字数 1486 浏览 6 评论 0原文

注意:在阅读所有这些附加内容之前,您可能需要跳到底部阅读实际问题。

我正在为 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查询可以帮助我解决这个问题?或者我认为这是它能得到的最好的结果是正确的吗?

Note: You might want to skip to the bottom to read the actual question before reading all this additional stuff.

I'm working on a ACL implementation for CakePHP. Mainly cause I'm trying to decouple it from AuthComponeny so I can use Authsome for my projects. I have the theory of implementation down but I've hit a little stumbling block.

Obviously I want to keep the number of database queries down to a minimum. So I'm asking here on the off chance that this is possible (I seriously doubt it is.)

Assuming a table structure like this:

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

And a few records to test (controllers is the root node and I might get the lft and rght values wrong):

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

And two test paths:

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

Giving these results, returning an array of ids from most relevant to least relevant:

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

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

What I'm currently doing is explode()ing the path into and array, (using $test1 for this example) first finding all records that match the alias "two_action"; then looping through the results and finding all records that match the parent id's of the last result and have the alias of "one_test_controller". Then repeat until parent_id = 0.

It works but obviously multiple recursive SQL queries are not ideal, is there a magic SQL query that can help me with this? Or am I right in assuming that this is the best it can get?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

翻身的咸鱼 2024-10-19 16:51:25

嗯?您已经获得了通过使用邻接树一次性解析路径来获取数据的结构。

但是,如果不存储完整路径/需要唯一的节点名称,则无法从下往上搜索。考虑一下 - 在您的两个测试用例中,您都从“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;

或使用 ids:

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_ids重建lft和rght)

eh? You've already got the structure to fetch the data by parsing the path in a single pass with the adjacency tree.

However without storing the full path / requiring unique node names, you can't search from the bottom up. Consider - in both your test cases you're starting with 'two_action' but looking for 2 different leafs. If you store the entire path in the table (or can reference the nodes by id from your query) then....

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;

or using ids:

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;

Alternatively, you could write a query to return every possible path which has a specific node alias - but that's not going to be very efficient either....

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;

(and its easy enought to rebuild lft and rght from the parent_ids)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文