8

我有一个树层次结构,它内置在一个表中,其 parent_id 指向前一个根节点。

我正在遍历所有根节点(root1、root2),并且我正在为 root1 和 child1 设置 root1 或 root1/child1 的路径。为了找到 child1 的路径,我必须至少进行 2 次调用来形成路径。有没有一种有效的方法来填充路径,因为我们处理了大量嵌套 5-7 层深的根节点和子节点。

create table foo (id, name, parent_id, path)
insert into foo (1, "root1', null, null)
insert into foo (2, "child1', 1, null)

root1 (path = null)
  child1 (path = root1)
    subchild1 (path = root1/child1)

root2
   child2
     subchild2
4

5 回答 5

11

正如您在问题中提到的那样,您可以使用存储过程,因为嵌套最多可以达到 7 级。

存储过程

CREATE PROCEDURE updatePath()
BEGIN
declare cnt, n int;
    select count(*) into n from foo where parent_id is null;
    update foo a, foo b set a.path = b.name where b.parent_id is null and a.parent_id = b.id;
    select count(*) into cnt from foo where path is null;
    while cnt > n do
        update foo a, foo b set a.path = concat(b.path, '/', b.name) where b.path is not null and a.parent_id = b.id;
        select count(*) into cnt from foo where path is null;
    end while;
END//

为了检查实际记录,我们刚刚打印了路径列中具有空值的普通记录

select * from foo

结果

| ID |         NAME | PARENT_ID |   PATH |
------------------------------------------
|  1 |        root1 |    (null) | (null) |
|  2 |       child1 |         1 | (null) |
|  3 |    subchild1 |         2 | (null) |
|  4 |       child2 |         1 | (null) |
|  5 |       child3 |         1 | (null) |
|  6 |    subchild2 |         4 | (null) |
|  7 | subsubchild1 |         6 | (null) |

调用程序

call updatepath

程序执行后的结果

select * from foo

结果

| ID |         NAME | PARENT_ID |                   PATH |
----------------------------------------------------------
|  1 |        root1 |    (null) |                 (null) |
|  2 |       child1 |         1 |                  root1 |
|  3 |    subchild1 |         2 |           root1/child1 |
|  4 |       child2 |         1 |                  root1 |
|  5 |       child3 |         1 |                  root1 |
|  6 |    subchild2 |         4 |           root1/child2 |
|  7 | subsubchild1 |         6 | root1/child2/subchild2 |

SQLFIDDLE

希望这可以帮助....

于 2013-04-07T10:17:28.153 回答
1

我真的很喜欢 Modified Preorder Tree Traversal。它允许您在单个查询中获取整个树的层次结构。这是一个详细的教程:http ://www.sitepoint.com/hierarchical-data-database-2/

如果您对 MPTT 有任何疑问,请告诉我,我很乐意为您提供帮助!

于 2013-04-08T12:41:16.143 回答
0

您可以考虑添加一个包含从每个树根到叶子的所有路径的闭包表。在 SQL 中维护图的传递闭包(从 1999 年开始)描述了一些理论背景。

关于分层数据的 stackoverflow审查问题描述了许多替代方法。在那里,Tegiri Nenashi指出了一个综合参考书目,其中包括RDBMS 中的分层数据

闭表的优点是查询效率高,解决方案不影响你当前的数据结构。您将需要扩展您的闭包表并在修改森林时对其进行维护。

您用短路径表示表中的大量项目。这使得闭包表的性能在适当的索引下几乎保持线性。您还可以在关闭表中保留每个节点的路径以避免重新计算。该方法对每个操作都有恒定数量的查询,并支持任何深度的层次结构。

于 2013-04-07T18:08:11.417 回答
0

您应该使用嵌套集模型 http://en.wikipedia.org/wiki/Nested_set_model

于 2013-04-09T11:59:19.723 回答
0

虽然在单个调用中严格来说不可能,但您可以隐藏多个调用,但将它们放入您从 SQL 调用的 MySQL 函数中,该函数返回父路径。

虽然这可能比在脚本中执行它更有效,但我不认为它会那么有效。

如果最大级别数是固定的,您可能会使用 JOIN,如下所示:-

SELECT foo.id, foo.name, CONCAT_WS(',', d.name, c.name, b.name, a.name)
FROM foo
LEFT OUTER JOIN foo a ON foo.parent_id = a.id
LEFT OUTER JOIN foo b ON a.parent_id = b.id
LEFT OUTER JOIN foo c ON b.parent_id = c.id
LEFT OUTER JOIN foo d ON c.parent_id = d.id

虽然这会起作用,但它的限制很大(即,如果最大级别数发生变化,您将不得不使用它来更改 SQL 的每一位),而且如果级别数不小,它将变得难以理解。

于 2013-04-02T16:07:09.743 回答