表示数据库中的树结构

时间:2009-06-24 22:43:11

标签: c# database data-structures tree

我已经了解了在邻接数据库中表示层次结构的各种方法,如邻接列表。

我决定尝试像这样的表格(过度简化)这样的直接方式:id | name | parent其中parent是id的内部引用。

这应该足以代表一个未定义深度的简单树。

现在,我如何构建一个树来表示这种数据结构?就像,如果我想生成XML或一系列嵌套<ul><li>以HTML格式打印它,循环节点的最有效方法是什么?我想知道Linq是否能够提供帮助,但我也对更一般(不是.NET界限)和理论答案感兴趣。

提前致谢。

1 个答案:

答案 0 :(得分:2)

具有讽刺意味的是,这个问题最难的部分可能是从数据库中获取满足特定路径谓词的约束记录集。除非您使用的是提供分层查询支持的数据库(如Oracle的CONNECT BY),否则会变得非常复杂。查看this SO question了解该部分的一些方法。

但是,让我们假设您已经设法加载数据......您可以将其添加到自定义对象集合,或者数据集或XML结构中(如果您不关心操作数据)除了作为字符串)。如果您有自定义对象,则可以通过查询整个数据集以查找与父键匹配的记录来添加公开子集合的属性(请参阅下面的示例)。如果记录数量很大,由于搜索集合的成本,这开始变得不具有性能。这是DataSet解决方案的亮点 - 因为您可以在ParentID列上添加自定义索引,从而提高查找性能。您可以read about Typed Data Sets在此处详细了解如何在此方向扩展我的示例。

以下是具有树导航支持的自定义对象的示例。我正在使用目录结构(文件夹),因为它是分层数据的自然示例。同样,要小心使用没有索引结构的代码,因为完整的子遍历将是O(n 2 )。

class Folder  // could be converted to a DataRow-derivative
{
    public int Id          { get; set; }
    public string Name     { get; set; }
    public int? ParentId   { get; set; }

    public IEnumerable<Folder> GetChildren( IEnumerable<Folder> allFolders )
    {
        // NOTE: becomes expensive on large hierarchies on unindexed data
        //   a slightly better version, could replace IEnumerable<Folder>
        //   with IOrderedEnumerable<Folder> which would allow the use of
        //   a binary search algorithm for finding children (assuming it's
        //   ordered on the ParentId property).
        return AllFolders.Where( x => x.ParentId.HasValue && 
                                      x.ParentId.Value = this.Id );
    }
}

// elsewhere in your code...

void LoadAndPrintFolders()
{
   // load the folder data... from database, or wherever
   IEnumerable<Folder> allFolders = LoadFolders();

   // find all root folders (let's say, those that have a null ParentId
   IEnumerable<Folder> rootFolders = 
          allFolders.Where( x => !x.ParentId.HasValue );

   // iterate over all of the data as a tree structure...
   foreach( var folder in rootFolders )
   {
       PrintFolder( allFolders, folder, 0 );
   }

}

// recursive function to print all folders...
void PrintFolder( IEnumerable<Folder> allFolders, Folder f, int level )
{
    Console.WriteLine( "{0}{1}", new string('\t',level), f.Name );
    foreach( var folder in f.GetChildren( allFolders )
    {
        PrintFolder( allFolders, folder, level + 1 );
    }
}