存储树状结构(上)─领接表方式

来源:互联网 发布:淘宝网安踏女鞋 编辑:程序博客网 时间:2024/06/06 15:37

我们经常需要在关系型数据库中保存一些树状结构数据,比如分类、分组、权限控制 ACL、论坛帖子树状回复等等。常用的方法有两种,一种是领接表的方式,另一种是预排序遍历树方式。

本文以 MySQL 和 PHP 为例进行说明,假设树状结构如下:
树结构图 

领接表方式

领接表方式主要依赖于一个 parent 字段,用于指向上级节点,将相邻的上下级节点连接起来,从而将整个树的各个节点连接起来。这样,我们例子中的节点变成行记录如下:
数据库结构
id 为自动递增自动,parent_id 为上级节点的 id。一目了然,“PHP”是“Language”的子节点。

接下来我们要显示树,PHP 代码也可以很直观,显示树的子节点的代码如下:

<?php/** * @param $parent_id 父节点 id,0 则显示整个树结构。 * @param $level 当前节点所处的层级,用于缩进显示节点。 */function show_children($parent_id 0$level 0){    // 获取父节点下的所有子节点    $result mysql_query('SELECT id, name FROM tree WHERE parent_id='.intval($parent_id));    // 显示每个子节点    while ($row mysql_fetch_array($result)) {        // 缩进显示        echo '<div style="margin-left:'.($level*12).'px">'.$row['name'].'</div>';        // 递归调用当前函数,显示再下一级的子节点        show_children($row['id'], $level+1);    }}?>

想要显示整个树结构,调用 show_children()。想要显示“Database”子树,则调用 show_children(2),因为“Database”的 id 是 2。

还有一个经常用到的功能是获取节点路径,即给出一个节点,返回从根节点到当前节点的路径。用函数实现如下:

<?php/** * @param $id 需要获取路径的当前节点的 id。 */function get_path($id) {    // 获取当前节点的父节点 id 和当前节点名    $result mysql_query('SELECT parent_id, name FROM tree WHERE id='.intval($id));    $row mysql_fetch_array($result);    // 使用此数组保存路径    $path = array();    // 将当前节点名保存进路径数组中    $path[] = $row['name'];    // 如果父节点非 0,即非根节点,则进行递归调用获取父节点的路径    if ($row['parent_id']) {        // 递归调用,获取父节点的路径,并且合并到当前路径数组的其它元素前边        $path array_merge(get_path($row['parent_id']), $path);    }    return $path;}?>

想要获取“MySQL 5.0”的路径,调用 get_path(4),4 即是这个节点的 id。

领接表方式的优点在于容易理解,代码也比较简单明了。缺点则是递归中的 SQL 查询会导致负载变大,特别是需要处理比较大型的树状结构的时候,查询语句会随着层级的增加而增加,WEB 应用的瓶颈基本都在数据库方面,所以这是一个比较致命的缺点,直接导致树结构的扩展困难重重。