2010-12-08 2 views
1

Я работаю над модулем DotNetNuke, который включает в себя меню навигации по дереву.Рекурсивный SQL для создания меню навигации DNN

До сих пор я работал, в том смысле, что дочерние узлы подключены к их правильным родительским узлам, но узлы-братья и сестры все еще не в порядке. Существует поле TabOrder, используемое для определения порядка братьев и сестер, но из-за рекурсии я не могу правильно их отсортировать.

Я пытаюсь сделать это в хранимой процедуре SQL Server, что может быть ошибкой, но я чувствую, что я настолько близок, что должно быть решение. Кто-нибудь знает, что я делаю неправильно?

Буду признателен за любые ваши идеи. Заранее спасибо.


Решение:

я, наконец, нашел решение на мой вопрос. Ключом было рекурсивно создать вкладку Lineage (TabLevel + TabOrder) с вкладки Root Tab на вкладки Leaf. Как только это было создано, я смог правильно заказать возвращаемые записи.

Однако, поскольку я возвращался, чтобы опубликовать это, я увидел ответ MarkXA, который, вероятно, является лучшим решением. Я не знал, что метод GetNavigationNodes даже существовал.

Я думаю, он прав, что использование GetNavigationNodes является более надежным решением, но пока я буду использовать свое решение на базе SQL. --Что я могу сказать? Я усердно учился.

Здесь:

ALTER procedure [dbo].[Nav_GetTabs] 
    @CurrentTabID int = 0 
AS 

--============================================================ 
--create and populate @TabLineage table variable with Tab Lineage 
-- 
--"Lineage" consists of the concatenation of TabLevel & TabOrder, concatenated recursively from the root to leaf. 
--The lineage is VERY important, making it possible to properly order the Tab links in the navigation module. 
--This will be used as a lookup table to match Tabs with their lineage. 
--============================================================ 
DECLARE @TabLineage table 
    (
     TabID  int, 
     Lineage  varchar(100) 
    ); 

WITH TabLineage AS 
(
    --start with root Tabs 
    SELECT T.TabID, T.ParentID, CAST(REPLICATE('0', 5 - LEN(CAST(T2.[Level] as varchar(10)) + CAST(T2.TabOrder as varchar(10)))) + CAST(T2.[Level] as varchar(10)) + CAST(T2.TabOrder as varchar(10)) as varchar(100)) AS Lineage 
     FROM Tabs T 
      INNER JOIN Tabs T2 ON T.TabID = T2.TabID 
      INNER JOIN TabPermission TP ON T.TabID = TP.TabID 
     WHERE T.ParentID IS NULL 
      AND T.IsDeleted = 0 
      AND T.IsVisible = 1 
      AND TP.RoleID = -1 

    UNION ALL 

    --continue recursively, from parent to child Tabs 
    SELECT T.TabID, T.ParentID, CAST(TL.Lineage + REPLICATE('0', 5 - LEN(CAST(T2.[Level] as varchar(10)) + CAST(T2.TabOrder as varchar(10)))) + CAST(T2.[Level] as varchar(10)) + CAST(T2.TabOrder as varchar(10)) as varchar(100)) AS Lineage 
     FROM Tabs T 
      INNER JOIN Tabs T2 ON T.TabID = T2.TabID 
      INNER JOIN TabPermission TP ON T.TabID = TP.TabID 
      INNER JOIN TabLineage TL ON T.ParentID = TL.TabID 
     WHERE T.IsDeleted = 0 
      AND T.IsVisible = 1 
      AND TP.RoleID = -1 
) 
--insert results of recursive query into temporary table 
INSERT @TabLineage 
    SELECT TL.TabID, TL.Lineage FROM TabLineage TL ORDER BY TL.Lineage 
    OPTION (maxrecursion 10); --to increase number of traversed generations, increase "maxrecursion" 


--============================================================ 
--create and populate @Ancestor table variable with @CurrentTab ancestors 
-- 
--"Ancestors" are Tabs following the path from @CurrentTab to the root Tab it's descended from (inclusively). 
--These are Tab links we want to see in the navigation. 
--============================================================ 
DECLARE @Ancestor table 
    (
     TabID  int 
    ); 

WITH Ancestor AS 
(
    --start with @CurrentTab 
    SELECT T.TabID, T.ParentID FROM Tabs T WHERE T.TabID = @CurrentTabID 

    UNION ALL 

    --continue recursively, from child to parent Tab 
    SELECT T.TabID, T.ParentID 
     FROM Ancestor A INNER JOIN Tabs T ON T.TabID = A.ParentID 
) 

--insert results of recursive query into temporary table 
INSERT @Ancestor 
    SELECT A.TabID FROM Ancestor A 
    OPTION (maxrecursion 10); --to increase number of traversed generations, increase "maxrecursion" 


--============================================================ 
--retrieve Tabs to display in navigation 

--This section UNIONs three query results together, giving us what we want: 
-- 1. All Tabs at Level 0. 
-- 2. All Tabs in @CurrentTab's lineage. 
-- 3. All Tabs which are children of Tabs in @CurrentTab's lineage. 
--============================================================ 
WITH TabNav (TabID, TabLevel, TabName, Lineage) AS 
(
    --retrieve all Tabs at Level 0 -- (Root Tabs) 
    (SELECT T.TabID, T.[Level] AS TabLevel, T.TabName, TL.Lineage 
    FROM Tabs T 
     INNER JOIN TabPermission TP ON (T.TabID = TP.TabID AND TP.RoleID = -1) 
     INNER JOIN @TabLineage TL ON T.TabID = TL.TabID 
    WHERE T.IsDeleted = 0 
     AND T.IsVisible = 1 
     AND T.[Level] = 0 

    UNION 

    --retrieve Tabs in @CurrentTab's lineage 
    SELECT T.TabID, T.[Level] AS TabLevel, T.TabName, TL.Lineage 
    FROM Tabs T 
     INNER JOIN TabPermission TP ON (T.TabID = TP.TabID AND TP.RoleID = -1) 
     INNER JOIN @Ancestor A ON T.TabID = A.TabID 
     INNER JOIN @TabLineage TL ON T.TabID = TL.TabID 
    WHERE T.IsDeleted = 0 
     AND T.IsVisible = 1 

    UNION 

    --retrieve Tabs which are children of Tabs in @CurrentTab's lineage 
    SELECT T.TabID, T.[Level] AS TabLevel, T.TabName, TL.Lineage 
    FROM Tabs T 
     INNER JOIN TabPermission TP ON (T.TabID = TP.TabID AND TP.RoleID = -1) 
     INNER JOIN @Ancestor A ON T.ParentID = A.TabID 
     INNER JOIN @TabLineage TL ON T.TabID = TL.TabID 
    WHERE T.IsDeleted = 0 
     AND T.IsVisible = 1) 
) 

--finally, return the Tabs to be included in the navigation module 
SELECT TabID, TabLevel, TabName FROM TabNav ORDER BY Lineage; 
--============================================================ 
+0

Запрос был полезен в том, что мне пришлось макетировать меню начальной загрузки. Sql был достаточно хорош, чтобы помочь мне генерировать теги списка. – Greg 2016-11-15 20:28:33

ответ

2

Ответ «не использовать SQL». Уже существует метод DotNetNuke.UI.Navigation.GetNavigationNodes, который делает это для вас, и если вы его используете, то ваш модуль не сломается, если и когда изменится схема базы данных. Даже если вам нужно сделать что-то, что GetNavigationNodes не будет обрабатывать, вам все равно лучше получить страницы через API, чтобы быть в будущем надежным. Переход непосредственно к базе данных - просто запрос на неприятности :)

1

здесь является плита котла (не на основе данного кода ФП в) пример рекурсивного дерева КТР, который показывает, как сортировать дерево:

DECLARE @Contacts table (id varchar(6), first_name varchar(10), reports_to_id varchar(6)) 
INSERT @Contacts VALUES ('1','Jerome', NULL) -- tree is as follows: 
INSERT @Contacts VALUES ('2','Joe' ,'1')  --      1-Jerome 
INSERT @Contacts VALUES ('3','Paul' ,'2')  --     /  \ 
INSERT @Contacts VALUES ('4','Jack' ,'3')  --    2-Joe   9-Bill 
INSERT @Contacts VALUES ('5','Daniel','3')  --   /  \    \ 
INSERT @Contacts VALUES ('6','David' ,'2')  --  3-Paul   6-David  10-Sam 
INSERT @Contacts VALUES ('7','Ian' ,'6')  -- / \   / \ 
INSERT @Contacts VALUES ('8','Helen' ,'6')  -- 4-Jack 5-Daniel 7-Ian 8-Helen 
INSERT @Contacts VALUES ('9','Bill ' ,'1')  -- 
INSERT @Contacts VALUES ('10','Sam' ,'9')  -- 

DECLARE @Root_id varchar(6) 

--get all nodes 2 and below 
SET @Root_id=2 
PRINT '@Root_id='+COALESCE(''''[email protected]_id+'''','null') 
;WITH StaffTree AS 
(
    SELECT 
     c.id, c.first_name, c.reports_to_id, c.reports_to_id as Manager_id, cc.first_name AS Manager_first_name, 1 AS LevelOf 
     FROM @Contacts     c 
      LEFT OUTER JOIN @Contacts cc ON c.reports_to_id=cc.id 
     WHERE [email protected]_id OR (@Root_id IS NULL AND c.reports_to_id IS NULL) 
    UNION ALL 
     SELECT 
      s.id, s.first_name, s.reports_to_id, t.id, t.first_name, t.LevelOf+1 
     FROM StaffTree   t 
      INNER JOIN @Contacts s ON t.id=s.reports_to_id 
    WHERE [email protected]_id OR @Root_id IS NULL OR t.LevelOf>1 
) 
SELECT * FROM StaffTree ORDER BY LevelOf, first_name 

ВЫВОД:

@Root_id='2' 
id  first_name reports_to_id Manager_id Manager_first_name LevelOf 
------ ---------- ------------- ---------- ------------------ ----------- 
2  Joe  1    1   Jerome    1 
6  David  2    2   Joe    2 
3  Paul  2    2   Joe    2 
5  Daniel  3    3   Paul    3 
8  Helen  6    6   David    3 
7  Ian  6    6   David    3 
4  Jack  3    3   Paul    3 

(7 row(s) affected) 

ключ LevelOf колонки. Посмотрите, как это просто буквально 1 при выборе основного родителя в CTE. Столбец LevelOf затем увеличивается в части UNION ALL рекурсивного CTE. Каждый рекурсивный вызов (не строка) в CTE будет удалять UNION ALL один раз и приращение. Не намного больше, чем это.

+0

Ваш пример похож на то, что я делаю. Мне нужно будет изучить его больше, чтобы узнать, поддерживает ли оно решение. Спасибо за ответ. – ks78 2010-12-08 16:32:01

+0

Вы используете DotNetNuke? Если это так, вы можете попробовать мою хранимую процедуру и передать ей TabID вкладки 3-4 уровня, чтобы увидеть мою проблему. – ks78 2010-12-08 16:40:23

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