【问题标题】:Store tree data structure in database在数据库中存储树数据结构
【发布时间】:2014-10-16 13:40:57
【问题描述】:

在上面的树中,每个节点都有一个名称和值。每个节点最多可以有 6 个子节点。如何将其存储在 MySQL 数据库中以高效执行以下操作?

运营

1) grandValue(node) - 应该给出(所有后代值的总和,包括自己)

例如,

  • grandValue(C) = 300
  • grandValue(I) = 950
  • grandValue(A) = 3100

2) children(node) - 应该给出所有孩子的列表(仅限直系后代)

例如,

  • children(C) = null
  • children(I) = L,M,N
  • children(A) = B,C,D,E

3) family(node) - 应该给出后代列表

  • family(C) = null
  • family(I) = L,M,N
  • family(A) = B,C,D,E,F,G,H,I,J,K,L,M,N

4) parent(node) - 应该给节点的父节点

  • parent(C) = A
  • parent(I) = D
  • parent(A) = null

5) insert(parent, node, value) - 应该插入节点作为父节点的子节点

  • insert(C, X, 500) 插入一个名为 X 的值为 500 的节点作为 C 的子节点

我正在考虑使用递归方法来进行这些操作,就像我们使用二叉树一样。但我不确定这是否是最佳方法。这棵树可能包含 10 到 3000 万个节点,并且可能是倾斜的。所以将数据转储到内存堆栈是我关心的领域。

请帮忙。

注意:我在 VPS 机器上使用 PHP、MySQL、Laravel。

更新:树会变大。新节点将被添加为叶节点或少于 6 个节点且不在 2 个节点之间的节点的子节点。

【问题讨论】:

标签: php mysql database algorithm data-structures


【解决方案1】:

您可以使用嵌套集将数据存储在表中。
http://en.wikipedia.org/wiki/Nested_set_model#Example
我担心如果您打算不断添加新项目,您的数百万个节点可能会让生活变得困难。也许可以通过使用有理数而不是整数作为左右值来减轻这种担忧。添加深度列以加快您要求后代的愿望。我编写了一些 SQL 来创建您要求的表和存储过程。我是在 SQL Server 中完成的,语法可能略有不同,但执行的是所有标准 SQL 语句。此外,我只是手动确定每个节点的上限和下限。显然,您必须编写代码才能将这些节点插入(和维护)到您的数据库中。

CREATE TABLE Tree(
    Node nvarchar(10) NOT NULL,
    Value int NOT NULL,
    L int NOT NULL,
    R int NOT NULL,
    Depth int NOT NULL,
);

INSERT INTO Tree (Node, Value, L, R, Depth) VALUES ('A', 100,  1, 28, 0);
INSERT INTO Tree (Node, Value, L, R, Depth) VALUES ('B', 100,  2,  3, 1);
INSERT INTO Tree (Node, Value, L, R, Depth) VALUES ('C', 300,  4,  5, 1);
INSERT INTO Tree (Node, Value, L, R, Depth) VALUES ('D', 150,  6, 25, 1);
INSERT INTO Tree (Node, Value, L, R, Depth) VALUES ('E', 200, 26, 27, 1);
INSERT INTO Tree (Node, Value, L, R, Depth) VALUES ('F', 400,  7,  8, 2);
INSERT INTO Tree (Node, Value, L, R, Depth) VALUES ('G', 250,  9, 10, 2);
INSERT INTO Tree (Node, Value, L, R, Depth) VALUES ('H', 500, 11, 12, 2);
INSERT INTO Tree (Node, Value, L, R, Depth) VALUES ('I', 350, 13, 21, 2);
INSERT INTO Tree (Node, Value, L, R, Depth) VALUES ('J', 100, 21, 22, 2);
INSERT INTO Tree (Node, Value, L, R, Depth) VALUES ('K',  50, 23, 24, 2);
INSERT INTO Tree (Node, Value, L, R, Depth) VALUES ('L', 100, 14, 15, 3);
INSERT INTO Tree (Node, Value, L, R, Depth) VALUES ('M', 300, 16, 17, 3);
INSERT INTO Tree (Node, Value, L, R, Depth) VALUES ('N', 200, 18, 19, 3);

CREATE PROCEDURE grandValue
    @Node NVARCHAR(10)
AS
BEGIN
    SET NOCOUNT ON;
    DECLARE @lbound INT;
    DECLARE @ubound INT;
    SELECT @lbound = L, @ubound = R FROM Tree WHERE Node = @Node
    SELECT SUM(Value) AS Total FROM TREE WHERE L >= @lbound AND R <= @ubound
    RETURN
END;

EXECUTE grandValue 'C';
EXECUTE grandValue 'I';
EXECUTE grandValue 'A';

CREATE PROCEDURE children
    @Node NVARCHAR(10)
AS
BEGIN
    SET NOCOUNT ON;
    DECLARE @lbound INT;
    DECLARE @ubound INT;
    DECLARE @depth INT;
    SELECT @lbound = L, @ubound = R, @depth=Depth FROM Tree WHERE Node = @Node
    SELECT Node FROM TREE WHERE L > @lbound AND R < @ubound AND Depth = (@depth + 1)
    RETURN
END;

EXECUTE children 'C';
EXECUTE children 'I';
EXECUTE children 'A';

CREATE PROCEDURE family
    @Node NVARCHAR(10)
AS
BEGIN
    SET NOCOUNT ON;
    DECLARE @lbound INT;
    DECLARE @ubound INT;
    SELECT @lbound = L, @ubound = R FROM Tree WHERE Node = @Node
    SELECT Node FROM TREE WHERE L > @lbound AND R < @ubound
    RETURN
END;

EXECUTE family 'C';
EXECUTE family 'I';
EXECUTE family 'A';

CREATE PROCEDURE parent
    @Node NVARCHAR(10)
AS
BEGIN
    SET NOCOUNT ON;
    DECLARE @lbound INT;
    DECLARE @ubound INT;
    DECLARE @depth INT;
    SELECT @lbound = L, @ubound = R, @depth = Depth FROM Tree WHERE Node = @Node
    SELECT Node FROM TREE WHERE L < @lbound AND R > @ubound AND Depth = (@depth - 1)
    RETURN
END;

EXECUTE parent 'C';
EXECUTE parent 'I';
EXECUTE parent 'A';

CREATE PROCEDURE ancestor
    @Node NVARCHAR(10)
AS
BEGIN
    SET NOCOUNT ON;
    DECLARE @lbound INT;
    DECLARE @ubound INT;
    SELECT @lbound = L, @ubound = R FROM Tree WHERE Node = @Node
    SELECT Node FROM TREE WHERE L < @lbound AND R > @ubound
    RETURN
END;

EXECUTE ancestor 'C';
EXECUTE ancestor 'I';
EXECUTE ancestor 'A';

首先要在表中创建嵌套集,您可以运行一些代码来生成插入或从第一个节点开始,然后依次添加每个附加节点 - 尽管因为每次添加都可能修改表中的许多节点设置时,在构建此数据库时可能会出现大量的数据库抖动。

这是一个将节点添加为另一个节点的子节点的存储过程:

CREATE PROCEDURE insertNode
    @ParentNode NVARCHAR(10), @NewNodeName NVARCHAR(10), @NewNodeValue INT
AS
BEGIN
    SET NOCOUNT ON;
    DECLARE @ubound INT;
    DECLARE @depth INT;
    SELECT @ubound = R, @depth = Depth FROM Tree WHERE Node = @ParentNode
    UPDATE Tree SET L = L + 2 WHERE L >= @ubound
    UPDATE Tree SET R = R + 2 WHERE R >= @ubound
    INSERT INTO Tree (Node, Value, L, R, Depth) VALUES (@NewNodeName, @NewNodeValue,  @ubound, @ubound + 1, @depth + 1);
    RETURN
END;

我从http://www.evanpetersen.com/item/nested-sets.html 那里得到这个,他还展示了一个很好的图形行走算法来创建初始 L 和 R 值。您必须增强它以跟踪深度,但这很容易。

【讨论】:

  • (+1) 我已经看到了类似的存储分层数据的技术,我没有时间测试它,但似乎很有希望。即使我使用 depthleftright 字段,我仍然会添加一个 int 键字段,而不是文本描述和 int 父字段...
  • @brett 谢谢!如何插入嵌套集模型?存储过程和php哪个更适合插入?
  • 我会使用存储过程。有一篇很好的文章在:link,但基本上有一些工作可以让树最初设置,尽管我想你可以从根节点开始并执行一堆添加。我将发布另一条评论,其中包含一个用于在其中添加节点的存储过程。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-10-30
  • 2014-09-12
  • 2010-09-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多