在上面的树中,每个节点都有一个名称和值。每个节点最多可以有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)
插入值为500的节点名称X作为C的孩子我正在考虑使用递归方法来执行这些操作,就像使用二叉树一样。但我不确定这是否是最佳方式。该树可能容纳10到3千万个节点并且可能会偏斜。因此,将数据转储到内存堆栈是我关注的领域。
请帮忙。
注意:我在VPS机器上使用PHP,MySQL,Laravel。
更新:树的大小会增加。新节点将作为叶子节点或节点的子节点添加,节点少于6个节点而不是2个节点之间。
答案 0 :(得分:4)
您可以使用嵌套集将数据存储在表中
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值。你必须加强这一点以跟踪深度,但这很容易。