我知道有两种方法:邻接表和嵌套树。据说由于大量查询,邻接表在遍历时会变得很慢。但我不知道任何现实的数字。我正在制作的网站将有 200 页左右。生成(例如)站点地图的遍历是否会花费超过 0.3 秒的时间?
在带有 LAMP 堆栈的 MySQL (innoDB) 上运行。
如果可能的话,我更喜欢实现邻接,因为设计更简单。
谢谢。
我知道有两种方法:邻接表和嵌套树。据说由于大量查询,邻接表在遍历时会变得很慢。但我不知道任何现实的数字。我正在制作的网站将有 200 页左右。生成(例如)站点地图的遍历是否会花费超过 0.3 秒的时间?
在带有 LAMP 堆栈的 MySQL (innoDB) 上运行。
如果可能的话,我更喜欢实现邻接,因为设计更简单。
谢谢。
除了你提到的两个,还有更多的选择。有:
请参阅我对“将平面表解析为树的最有效/优雅的方法是什么? ” 的回答。
或者几本书:
在 MySQL 中管理分层数据一文对此进行了详细介绍。
我会推荐“嵌套集”技术,因为它允许您在一个查询中获取整棵树(及其子树)。基本上读取很便宜,但写入很昂贵,因为必须重新平衡整个树。但如果你有 99% 的阅读量,那么它是完全合理的。
解析邻接列表的简单方法需要大量查询,并且对于大型列表可能需要大量时间来构建内存。作为参考,我所指的幼稚方法可以概括为:选择所有没有父项的项目,然后为每个项目递归地获取它的子项。这种方法需要 n+1 个数据库查询。
我使用以下方法来构建具有 1 个查询的邻接列表。从数据库中选择所有项目。将所有项目传输到由其键索引的数组中。遍历数组并将父对象的引用分配给它的每个子对象。再次遍历数组并移除所有子对象,只留下根级对象。
由于您提到了 LAMP 堆栈,因此执行此操作的 PHP 代码大致如下:
<?php
// Assumes $src is the array if items from the database.
$tmp = array();
// Traverse the array and index it by id, ensuing each item has an empty array of children.
foreach ($src as $item) {
$item['children'] = array();
$tmp[$item['id']] = $item;
}
// Now traverse the array a second time and link children to their parents.
foreach ($tmp as $id => $item) {
if ($item['parent_id'] != 0 || $item['parent_id'] !== NULL) {
$tmp[$item['parent_id']]['children'][$id] = &$tmp[$id];
}
}
// Finally create an array with just root level items.
$tree = array();
foreach ($tmp as $id => $item) {
if ($item['parent_id'] == 0 || $item['parent_id'] === NULL) {
$tree[$id] = $item;
}
}
// $tree now contains our adjacency list in tree form.
?>
请注意,此代码旨在说明从单个数据库查询构建邻接列表的技术。它可能会针对更少的内存消耗等进行优化。它还没有经过测试。
吉姆,
我认为另一种方法称为“嵌套集”,而不是“嵌套树”。
无论如何,站点地图的一个好处是您可能知道它的最大深度。我认为邻接模型的问题在于相应的 SQL 一次只在一个级别上工作,所以如果你有“n”个级别,那么你需要一个循环“n”个 SQL 语句......但我认为(I' m不确定)如果您提前知道最大'n',那么您可以编写相应的固定数量的多级SQL。
0.3 秒对我来说听起来像是一个很长的时间来计算 200 页,所以这可能没关系。
站点地图也不会经常更新;因此,即使从 SQL 中检索确实需要很长时间,您也可以将检索/计算的树缓存在 RAM 中。
或者,不用担心构建树的 SQL,您可以尽可能简单地存储它(作为邻接列表),从数据库中检索它作为一组简单的行,然后在 RAM 中构建树(使用循环您的高级编程语言)而不是在 SQL 中使用循环来使用 SQL 语句构建树。
为了完整起见:Oracle 有START_WITH
andCONNECT_BY
运算符:请参阅此Hierarchical Queries文档。