我正在使用一个数据库,用于以组件树的形式存储有关实体的信息。例如,它可能有一个带有儿童空调和发动机的camry物体。发动机可能有儿童活塞,空调也有儿童通风口。
这个想法是用户可以自定义创建这样的“模板”,然后根据需要用于实例化camry“树”。因此,用户可能首先创建模板,然后使用它将10个这些camry树添加到工作室,通过选择“添加新车”,选择“camry”,然后选择名称来存储每个模板的唯一数据。
如何在数据库中存储这样的构造,是否有任何简单的方法来实例化这样的树?
答案 0 :(得分:-1)
我之前已经完成了前半部分,所以我们从那里开始(方便,没有?)。在不了解您的需求的情况下,我建议将以下内容作为基础(您可以根据需要调整列宽):
CREATE TABLE tree (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
parent_id INT UNSIGNED NOT NULL DEFAULT 0,
type VARCHAR(20) NOT NULL,
name VARCHAR(32) NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY (parent_id, type, name),
KEY (parent_id)
);
为什么我这样做?好吧,让我们来看看每个领域。 id
是一个全局唯一值,我们可以使用它来标识此元素以及直接依赖于它的所有元素。 parent_id
让我们回到树上直到我们到达parent_id == 0
,这是树的顶部。 type
将是您的"汽车"或者"发泄"说明。 name
会让你符合type
的资格,所以像"凯美瑞"和"左驾驶员" (对于"发泄"显然)。
数据将与以下值一起存储:
INSERT INTO tree (parent_id, type, name) VALUES
(0, 'car', 'Camry'),
(1, 'hvac', 'HVAC'),
(2, 'vent', 'Driver Front Footwell'),
(2, 'vent', 'Passenger Front Footwell'),
(2, 'vent', 'Driver Rear Footwell'),
(2, 'vent', 'Passenger Rear Footwell'),
(1, 'glass', 'Glass'),
(7, 'window', 'Windshield'),
(7, 'window', 'Rear Window'),
(7, 'window', 'Driver Front Window'),
(7, 'window', 'Passenger Front Window'),
(7, 'window', 'Driver Rear Window'),
(7, 'window', 'Passenger Rear Window'),
(1, 'mirrors', 'Mirrors'),
(14, 'mirror', 'Rearview Mirror'),
(14, 'mirror', 'Driver Mirror'),
(14, 'mirror', 'Passenger Mirror');
我可以坚持下去,但我认为你明白了。只是为了确定...所有这些值都会产生一个看起来像这样的树:
(1, 0, 'car', 'Camry')
| (2, 1, 'hvac', 'HVAC')
| +- (3, 2, 'vent', 'Driver Front Footwell')
| +- (4, 2, 'vent', 'Passenger Front Footwell')
| +- (5, 2, 'vent', 'Driver Rear Footwell')
| +- (6, 2, 'vent', 'Passenger Rear Footwell')
+- (7, 1, 'glass', 'Glass')
| +- (8, 7, 'window', 'Windshield')
| +- (9, 7, 'window', 'Rear Window')
| +- (10, 7, 'window', 'Driver Front Window')
| +- (11, 7, 'window', 'Passenger Front Window')
| +- (12, 7, 'window', 'Driver Rear Window')
| +- (13, 7, 'window', 'Passenger Rear Window')
+- (14, 1, 'mirrors', 'Mirrors')
+- (15, 14, 'mirror', 'Rearview Mirror')
+- (16, 14, 'mirror', 'Driver Mirror')
+- (17, 14, 'mirror', 'Passenger Mirror')
现在,困难的部分:复制树。由于parent_id
引用,我们无法执行类似INSERT INTO ... SELECT
的操作;我们沦为必须使用递归函数。我知道,我们正在进入肮脏的地方。我打算伪代码,因为你没有注意到你正在使用哪种语言。
FUNCTION copyTreeByID (INTEGER id, INTEGER max_depth = 10, INTEGER parent_id = 0)
row = MYSQL_QUERY_ROW ("SELECT * FROM tree WHERE id=?", id)
IF NOT row
THEN
RETURN NULL
END IF
IF ! MYSQL_QUERY ("INSERT INTO trees (parent_id, type, name) VALUES (?, ?, ?)", parent_id, row["type"], row["name"])
THEN
RETURN NULL
END IF
parent_id = MYSQL_LAST_INSERT_ID ()
IF max_depth LESSTHAN 0
THEN
RETURN
END IF
rows = MYSQL_QUERY_ROWS ("SELECT id FROM trees WHERE parent_id=?", id)
FOR rows AS row
copyTreeByID (row["id"], max_depth - 1, parent_id)
END FOR
RETURN parent_id
END FUNCTION
FUNCTION copyTreeByTypeName (STRING type, STRING name)
row = MYSQL_QUERY_ROW ("SELECT id FROM tree WHERE parent_id=0 AND type=? AND name=?", type, name)
IF NOT ARRAY_LENGTH (row)
THEN
RETURN
END IF
RETURN copyTreeByID (row["id"])
END FUNCTION
copyTreeByTypeName
查找匹配的type
和name
的树ID,并将其传递给copyTreeByID
。这主要是一个实用功能,可以帮助您按类型/名称复制内容。
copyTreeByID
是真正的野兽。害怕它,因为它是递归和邪恶的。为什么递归?因为你的树木是不可预测的,可以是任何深度。但是没关系,我们有一个变量来跟踪深度并限制它(max_depth
)。所以让我们来看看吧。
首先抓取元素的所有数据。如果我们没有获得任何数据,请返回。使用元素type
和name
以及传递的parent_id
重新插入数据。如果查询失败,请返回。将parent_id
设置为最后一个插入ID,以便稍后传递它。检查max_depth
是否小于零,表示我们已达到最大深度;如果我们有回报从树中获取父级为id
的所有元素。然后,对于每个元素,递归到copyTreeByID
传递元素的id
,max_depth
减去1和新的parent_id
。最后返回parent_id
,以便您可以访问元素的新副本。
有意义吗? (我把它读回来,它有意义,而不是那意味着什么)。