2009-03-12 4 views
2

У меня есть таблица с pageId, parentPageId, столбцы заголовков.Возвращает неупорядоченный список из иерархических данных sql

Есть ли способ вернуть неупорядоченный вложенный список, используя asp.net, cte, хранимую процедуру, UDF ... что-нибудь?

Таблица выглядит следующим образом:

PageID ParentId Title 
1   null  Home 
2   null  Products 
3   null  Services 
4   2   Category 1 
5   2   Category 2 
6   5   Subcategory 1 
7   5   SubCategory 2 
8   6   Third Level Category 1 
... 

Результат должен выглядеть следующим образом:

Home 
Products 
    Category 1 
     SubCategory 1 
      Third Level Category 1 
     SubCategory 2 
    Category 2 
Services 

В идеале, список должен содержать <a> тегов, а также, но я надеюсь, что я могу добавить это самому, если я найду способ создать список <ul>.

EDIT 1: Я думал, что уже есть решение для этого, но, похоже, нет. Я хотел, чтобы он был максимально простым и бежал с помощью меню ASP.NET любой ценой, потому что по умолчанию он использует таблицы. Тогда я должен использовать CSS адаптеры и т.д.

Даже если я решу пойти вниз «меню ASP.NET» маршрут я смог найти только такой подход: http://aspalliance.com/822 который использует DataAdapter и DataSet :(

Любые более современный и эффективный способ?

+0

Итак, давайте посмотрим на попытки TSQL, иначе люди могут подумать, что это вопрос plzsendzmethecodez ... –

+0

Я не продвинулся слишком далеко. Я получил тот же результат, что и Брайан, которого я не знаю, что делать. – Milan

ответ

2

Лучшая практика будет делать это с помощью IHierarchyData и IHierarchalEnumerable и DataBind для пользовательского элемента управления, который наследуется от HierarchalDataBoundControl (это основа для управления, как TreeView).

Однако, давайте попробуем для быстрого и грязного, особенно эффективный, простой пример в C#:

//class to hold our object graph in memory 
//this is only a good idea if you have a small number of items 
//(less than a few thousand) 
//if so, this is a very flexible and reusable way to represent your tree 
public class Page 
{ 
    public string Title {get;set;} 
    public int ID {get;set;} 
    public Collection<Page> Pages = new Collection<Page>(); 

    public Page FindPage(int id) 
    { 
     return FindPage(this, id); 
    } 

    private Page FindPage(Page page, int id) 
    { 
     if(page.ID == id) 
     { 
      return page; 
     } 
     Page returnPage = null; 
     foreach(Page child in page.Pages) 
     { 
      returnPage = child.FindPage(id); 
      if(returnPage != null) 
      { 
       break; 
      } 
     } 
     return returnPage; 
    } 
} 

//construct our object graph 
DataTable data = SelectAllDataFromTable_OrderedByParentIDAscending(); 
List<Page> topPages = new List<Page>(); 
foreach(DataRow row in data.Rows) 
{ 
    Page page = new Page(); 
    page.Title = (string)row["Title"]; 
    page.ID = (int)row["PageID"]; 
    if(row["ParentID"] == null) 
    { 
     topPages.Add(page); 
    } 
    else 
    { 
     int parentID = (int)row["ParentID"]; 
     foreach(Page topPage in topPages) 
     { 
      Page parentPage = topPage.FindPage(parentID); 
      if(parentPage != null) 
      { 
       parentPage.Pages.Add(page); 
       break; 
      } 
     } 
    } 
} 

//render to page 
public override void Render(HtmlTextWriter writer) 
{ 
    writer.WriteFullBeginTag("ul"); 
    foreach(Page child in topPages) 
    { 
     RenderPage(writer, child); 
    } 
    writer.WriteEndTag("ul"); 
} 

private void RenderPage(HtmlTextWriter writer, Page page) 
{ 
    writer.WriteFullBeginTag("li"); 
    writer.WriteBeginTag("a"); 
    writer.WriteAttribute("href", "url"); 
    writer.Write(HtmlTextWriter.TagRightChar); 
    writer.Write(page.Title); 
    writer.WriteEndTag("a"); 
    if(page.Pages.Count > 0) 
    { 
     writer.WriteFullBeginTag("ul"); 
     foreach(Page child in page.Pages) 
     { 
      RenderPage(writer, child); 
     } 
     writer.WriteEndTag("ul"); 
    } 
    writer.WriteEndTag("li"); 
} 
+0

Конечная цель состоит в том, чтобы иметь навигационную структуру ul, которая может быть с CSS. Как вы рекомендуете заполнять сборку со всеми страницами, чтобы я мог ее перебирать. Может ли рекурсивный foreach (Страница p в c.Pages) создать строку для неэффективности? У меня не много строк, но все же ... – Milan

+0

@Milan приведенный мной пример кода заполняет структуру таким образом. Пока вы используете буферизованный писатель, такой как StringBuilder или HtmlTextWriter, чтобы генерировать конечный результат, он чрезвычайно эффективен. Имейте в виду, что ASP.NET разрывает массивные строки HTML для отображения страницы! –

+0

@ Милан в вашем первоначальном вопросе, в котором вы, по сути, описываете рекурсию. В SQL2005 нет хорошего способа рекурсии, обычно лучше получить прямой набор данных и организовать его в иерархию объектов на стороне .NET, что я и сделал выше. –

0

Это должно вас начать.

with x (pageID, title) 
     as (
    select cast(title as varchar(100)),pageID 
    from pages 
    where parentID is null 
    union all 
    select cast(x.title||' - '||e.title as varchar(100)), 
     e.pageID 
    from pages e, x 
    where e.parentID = x.pageID 
) 
    select title as title_tree 
    from x 
    order by 1 

Выход:

TITLE_TREE 
Home 
Products 
Services 
Products - Category 1 
Products - Category 2 
Products - Category 2 - Subcategory 1 
Products - Category 2 - Subcategory 1 - Third Level Category 1 
Products - Category 2 - Subcategory 2 
+0

Спасибо за ваши усилия, но я пришел к тому же решению, и теперь я застрял ... Я не знаю, что делать со структурой вроде этого. – Milan

0

Рассматривали ли вы получать вывод XML из SQL Server с помощью SELECT ... FOR XML EXPLICIT? Ваши данные, кажется, созданы для этого.

Для примера:

http://www.eggheadcafe.com/articles/20030804.asp

Если вы хотите продолжить, я мог бы работать на примере.

+0

XML в сочетании с XSLT, чтобы преобразовать его в список ul, также может быть хорошим. Я только не знаю, как преобразования xslt влияют на производительность. Также, если мы создадим хороший XML, мы можем объединить выходные данные с решением Rex и десериализовать XML непосредственно на объекты .net. Правильно? – Milan

+0

@ Milan Я предлагаю вам работать с обработкой вне sql-сервера, так как вы все равно захватываете весь набор результатов - меньше нагрузки на sql – eglasius

5

Использование Linq2Sql вы можете сделать:

List<PageInfo> GetHierarchicalPages() 
{ 
    var pages = myContext.PageInfos.ToList(); 
    var parentPages = pages.Where(p=>p.ParentId == null).ToList(); 
    foreach(var page in parentPages) 
    { 
     BuildTree(
     page, 
     p=> p.Pages = pages.Where(child=>p.pageId == child.ParentId).ToList() 
     ); 
    } 
} 
void BuildTree<T>(T parent, Func<T,List<T>> setAndGetChildrenFunc) 
{ 
    foreach(var child in setAndGetChildrenFunc(parent)) 
    { 
     BuildTree(child, setAndGetChildrenFunc); 
    } 
} 

Предполагая, что вы определяете свойство Страницы в PageInfo как:

public partial class PageInfo{ 
    public List<PageInfo> Pages{get;set;} 
} 

Обработка, чтобы получить его на иерархии происходит на стороне веб-приложений, что позволяет избежать дополнительной нагрузки на сервер sql. Также обратите внимание, что этот тип информации является идеальным кандидатом на кеш.

Вы можете сделать рендер как упомянутый Рекс.В качестве альтернативы вы можете немного расширить эту реализацию и обеспечить поддержку интерфейсов иерархии и использовать элементы управления asp.net.

Update 1: Для изменения рендеринга вы задали на комментарий, вы можете:

var sb = new System.IO.StringWriter(); 
var writer = new HtmlTextWriter(sb); 
// rex's rendering code 
var html = sb.ToString(); 
+0

+1 Очень интересно –

0

RexM - во-первых, я должен сказать, что я фронтальным разработчик так не может даже коснитесь вас навыками и знанием кодирования C#. Однако - я реализовал ваше решение с использованием объекта «Страница» и столкнулся с проблемой. Да, извините, что я пишу «pleaseSendMeTheCode» в этом случае, но никогда не думал, что важно детализировать «ошибку».

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

Мое меню имеет следующие поля данных: PageId, ParentID, PageOrder, PAGETITLE

заказ Страница относится к порядку, в котором страницы появляются в узле.

Так что мой запрос для SelectAllDataFromTable_OrderedByParentIDAscending(); был:

SELECT * FROM [pages] ORDER BY [parentID] ASC, [pageOrder] ASC 

Затем я использую jsTree, чтобы пункты меню перетаскивать и Droppable.

я вновь заказал несколько страниц и обнаружил ошибку:

Say моя структура похожа так:

home 
    cars 
    usa 
     muscle cars 
     suvs 
    europe 
    colours 
    directions 
    vertical 
    horizontal 
     up 
     down 

Если я двигаюсь «автомобили» (и все это дети) внутри «вниз» , дети из «автомобилей» больше не отображаются в меню. Это «ошибка».

Я проверил дб и ParentID и PageOrder все правильно под «авто», я также попытался изменить свой SQL запрос, начиная с нуля, все виды тестирования непосредственно на БД (все выше jsTree отключен, поэтому я вижу основной вложенный UL), но без успеха.

Просто интересно, как я видел другие форумы, указывающие на эту страницу для решений по превращению иерархических данных sql в вложенные UL, возможно, стоит кого-то заглянуть в нее.

Как мой сайт базируется на использовании Javascript, теперь я реализовал решение JQuery.ajax (которое очень плохо комментируется здесь) для создания вложенного UL, но, как я уже сказал, просто помечено как Потенциальная проблема.

Большое спасибо за то, что я начал свой собственный поиск решения!

Смежные вопросы