【问题标题】:Representing a tree structure out of a db从数据库中表示树结构
【发布时间】:2009-06-24 22:43:11
【问题描述】:

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

我决定尝试一种直截了当的方式,比如像这样完成的表格(过于简化):id | name | parent 其中 parent 是对 id 的内部引用。

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

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

提前致谢。

【问题讨论】:

标签: c# database data-structures tree


【解决方案1】:

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

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

这是一个支持树形导航的自定义对象示例。我使用的是目录结构(文件夹),因为它是分层数据的自然示例。同样,请注意在没有索引结构的情况下使用此代码,因为完整的子遍历将是 O(n2)。

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 );
    }
}

【讨论】:

    猜你喜欢
    • 2023-03-14
    • 2010-10-30
    • 2013-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-17
    • 2023-04-02
    • 1970-01-01
    相关资源
    最近更新 更多