Представление древовидной структуры из БД

Я читал о различных способах представления иерархической структуры в реляционной базе данных, такой как список смежности.

Я решил попробовать прямолинейный способ, например, таблицу (упрощенную), сделанную следующим образом: id | name | parent, где родитель является внутренней ссылкой на id.

Этого должно быть достаточно, чтобы представить простое дерево неопределенной глубины.

Теперь, как я могу построить дерево для представления такой структуры данных? Например, если я хочу создать XML или серию вложенных <ul><li> для печати в формате HTML, каков наиболее эффективный способ перебора узлов? Интересно, может ли Linq помочь, но меня также интересуют более общие (не связанные с .NET) и теоретические ответы.

Заранее спасибо.


person pistacchio    schedule 24.06.2009    source источник
comment
Вот несколько замечательных ответов: -tree" title="Какой самый эффективный и элегантный способ разобрать плоскую таблицу в дерево"> stackoverflow.com/questions/192220/   -  person Tomalak    schedule 25.06.2009


Ответы (1)


По иронии судьбы, самой сложной частью этой проблемы может быть получение ограниченного набора записей из базы данных, которые удовлетворяют определенному предикату пути. Если вы не используете базу данных, обеспечивающую поддержку иерархических запросов (например, Oracle CONNECT BY), это может стать довольно сложным. Проверьте этот ТАК вопрос для некоторых подходов с этой стороны.

Но давайте предположим, что вам удалось загрузить свои данные... вы можете сделать это либо в пользовательскую коллекцию объектов, либо в набор данных, либо в структуру XML (если вам не нужно манипулировать данными, кроме как струны). Если у вас есть настраиваемый объект, вы можете добавить свойство, которое предоставляет дочернюю коллекцию, запросив весь набор данных на наличие записей, совпадающих с родительским ключом (см. пример ниже). Если количество записей велико, это становится неэффективным из-за затрат на поиск в коллекции. Именно здесь решение DataSet сияет, так как вы можете добавить пользовательские индексы в столбец ParentID, которые повысят производительность поиска. Вы можете прочитать о типизированных наборах данных здесь, чтобы узнать больше о том, как расширить пример ниже в этом направлении.

Вот пример пользовательского объекта с поддержкой навигации по дереву. Я использую структуру каталогов (папки), потому что это естественный пример иерархических данных. Опять же, будьте осторожны при использовании этого кода без индексированной структуры, так как полный дочерний обход будет 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 );
    }
}
person LBushkin    schedule 25.06.2009