2009-01-30 42 views
2

我有一個菜單系統的表,它具有以下結構和一些數據。菜單系統的遞歸SQL

 
ID, Text, ParentID, DestinationID 
1, Applications, (null), (null) 
2, Games, (null), (null) 
3, Office, 1, (null) 
4, Text Editing, 1, (null) 
5, Media, (null), (null) 
6, Word, 3, 1 
7, Excel, 3, 2 
8, Crysis, 2, 3 

我需要的是一個查詢,我可以通過菜單ID,它將返回有ID的子項的列表。但是,我需要它僅返回具有到目的地的有效路徑的兒童。所以在上面的例子中,當用戶選擇應用程序時,他將被初始化爲(應用程序,遊戲),他被呈現給(Office)。文本編輯和媒體應該被省略,因爲它們下面沒有有效的目的地。

這個最棘手的事情是,沒有任何給定菜單的預定深度。

編輯

今天,問題就來了,對於MS SQL 2008年,但在過去的2個星期,我一直需要SQLite和SQL CE類似的解決方案。理想的解決方案不應該綁定到任何特定的SQL引擎。

+0

DB什麼您使用的是? – 2009-01-30 13:53:43

+0

如何在導航中獲得當前深度? – 2009-01-30 13:53:56

+0

導航的實際深度是無關緊要的。每次迭代都被視爲一個菜單。系統從空開始,並選擇具有空父母的所有菜單項。進行選擇時,當前菜單具有該ID,並用於過濾該菜單的項目。 – RichieACC 2009-01-30 13:59:28

回答

3

在Oracle:

SELECT m.*, level 
FROM my_table m 
START WITH 
    Id = :startID 
CONNECT BY 
    ParentID = PRIOR Id 
    AND DestinationID IS NOT NULL 

沒有辦法用一個單一的查詢做的ANSI SQL。您可以爲您的表創建一個附加列AccessPath

ID, Text, ParentID, DestinationID AccessPath 
1, Applications, (null), (null), "1" 
2, Games, (null), (null), "2" 
3, Office, 1, (null), "1.3" 
4, Text Editing, 1, (null), "1.4" 
5, Media, (null), (null), "5" 
6, Word, 3, 1, "1.3.6" 
7, Excel, 3, 2, "1.3.7" 
8, Crysis, 2, 3, "1.2.8" 

,並查詢:

SELECT mp.Id, mp.Text 
FROM my_table mp, my_table mc 
WHERE mp.parentID = @startingParent 
AND mc.Id <> mp.Id 
AND SUBSTR(mc.AccessPath, LENGTH(mp.AccessPath)) = mp.AccessPath 
GROUP BY 
mp.Id, mp.Text 

這是一個糟糕的主意,開始與NULL,作爲ParentID指數不能在這種情況下使用。首先,使用0而不是NULL的假parentID

+0

如果不是** WHERE COALESCE(mp.ParentID,0)= COALESCE(@ currentParent,0)**?當我使用MC時,我得到「多部分標識符」mc.ParentID「無法綁定。」改變它似乎工作,但我不明白爲什麼!根本沒有參考dest字段。 – RichieACC 2009-01-30 14:25:54

0

SQL不是很擅長處理任意深度的層次結構。

如果這些記錄少於1000條,我會將它們全部抓到應用程序並在那裏構建圖形。

如果這些記錄超過1000個,我會將它們分組到大約1000個原始子樹中(通過添加一個SubtreeID外鍵)並獲取每個子樹...然後在應用程序中構建子樹的圖形。

0

我會做的第一件事就是去掉目標列 - 它在層次結構方面沒有意義(它實際上似乎是一種第二個主鍵,用來表示您的子行按您代表的方式發送)

這將給

ID, Item, parentID 
1, Applications, (null) 
2, Games, (null) 
3, Office, 1 
4, Text Editing, 1 
5, Media, (null) 
6, Word, 3 
7, Excel, 3 
8, Crysis, 2 

如..

字>辦公>應用程序和...

的Excel>辦公>應用

...應該大概是在同一個菜單項(父ID 3)

我不知道你是如何選擇的菜單,但我會努力原則上有一個初始菜單按鈕設置爲(null),因爲它的參數和每個後續點擊保持動態順序下一個參數(這似乎符合您的意見)

eg

點擊頂層菜單: - 值是(空)

點擊應用: - 值是1次

點擊辦公室: - 值爲3

假設目標-ID除了顯示活動的兒童鏈接(允許您刪除它)之外什麼也沒做,代碼將如下:

with items (nodeID, PID, list) as 
    (select id, ParentID, item 
    from menu 
    where id = 9 
    union all 
    select id, ParentID, item 
    from menu 
    inner join items on nodeID = menu.ParentID 
) 
select * 
from items 
where (pid = 9) 
and nodeID in (select parentid from menu) 

這個工作在MSSQL 2005+

如果你需要一些其他的原因,目的ID,那麼你可以按如下(修改代碼,如果你需要返回,其中一個節點ID沒有被設置爲最低等級例如父母標識):

with items (nodeID, PID, list, dest) as 
    (select id, ParentID, item, destinationID 
    from menu 
    where id = 9 
    union all 
    select id, ParentID, item, destinationID 
    from menu 
    inner join items on nodeID = menu.ParentID 
) 
select * 
from items 
where (pid = 9) 
and (nodeID in (select parentid from menu) 
    or dest is not null) 
1

正如其他人指出的那樣,標準ANSI SQL無法實現您想要的功能。對於這樣的事情,我曾經在SQL 2000上實現了一個跟蹤前僱主製造的產品組件的系統 - 每個「產品」可以是螺絲A500等原子組件。該組件可用於「複合」組件:一些A500螺絲加上6個B120木板符合C90「時尚工具箱」。那個盒子,加上更多的螺絲和電機「M500」可以符合一個木製工具。

我設計了一個表格 「產品」 是這樣的:

ID, PartName, Description 
1, A500, "Screw A500" 
2, B120, "Wood panel B120" 
3, C90, "Stylish tool box C90" 
4, M500, "Wood cutter M500" 

和一個 「ProductComponent」 表如下:

Hierarchy, ComponentID, Amount 
0301, 1, 24 
0302, 2, 6 
0401, 1, 3 
0402, 3, 1 
0403, 4, 1 
040201, 1, 24 
040202, 2, 6 

的訣竅是:場層次是一個VARCHAR與前2個字符代表每個產品的ID,每個下一對字符標識樹中的一個節點。所以我們看到產品3取決於其他兩種產品。產品4取決於其他兩個,另外,其中一個取決於其他兩個。

該模型有很多冗餘,但可以輕鬆計算出特定產品需要多少螺絲,快速確定哪些部件需要木板或獲取產品最終依賴的所有組件列表(包括間接依賴關係)等等。在特定級別下掃描樹是一個簡單的LIKE查詢!

通過在十六進制表示中使用2個字符,我限制了一個產品直接取決於最多256個其他prod(這反過來可以依賴於別的東西)。如果你需要更多的信息,你可以改變它以使用基數36(26個字母加10個數字)或base-64。

另外,這個表模型在Access和mySQL上工作得很好。你不可能有任何方式的循環依賴。

3

如果您在數據庫中建立的層級/樹不會經常更改,我建議使用修改的預置樹遍歷(MPTT)算法。這將需要一個不同的表模式,但是可以讓你用一個簡單的SQL語句來請求整個子樹(沒有遞歸等)。

關於Storing Hierarchical Data in a Database的文章詳細描述了這種方法。

在你的榜樣,你會得到下面的樹,在這裏我所說的紅色數字的值和節點的綠色權值。現在

alt text

,如果你想選擇辦公室子樹,你可以這樣做:

SELECT * FROM tree WHERE left BETWEEN 10 AND 15 AND destination IS NOT NULL 

如果您的數據庫不支持BETWEEN語句,你當然可以寫的左> 10,然後左轉< 15。

你的表是這樣的:

name   | left | right | destination 
------------------------------------------ 
root   | 1 | 17 | NULL 
Applications | 7 | 16 | ... 
... 
0

https://geeks.ms/jirigoyen/2009/05/22/recursividad-con-sql-server/

ALTER PROCEDURE [dbo].[Usuarios_seguridad_seleccionar] 
AS 
BEGIN  

    DECLARE @minClave int 
    SELECT @minClave = MIN(Clave) FROM dbo.Usuarios_seguridad; 

    WITH UsuariosAccesos AS(

     SELECT top 1 us1.Padre,us1.Clave,us1.Variable,us1.Modulo,us1.Contenido,us1.Acceso,us1.Imagen 
     FROM dbo.Usuarios_seguridad us1 
     WHERE us1.Clave = @minClave 
     UNION ALL 
     SELECT top 100 percent us2.Padre,us2.Clave,us2.Variable,us2.Modulo,us2.Contenido,us2.Acceso,us2.Imagen 
     FROM dbo.Usuarios_seguridad us2 
     INNER JOIN UsuariosAccesos AS us3 ON us3.Clave = us2.Padre 
     WHERE us2.Clave <> @minClave 
    ) 

    SELECT TOP 100 PERCENT ia.Padre,ia.Clave,ia.Variable,ia.Modulo,ia.Contenido,ia.Acceso,ia.Imagen 
    FROM UsuariosAccesos ia 
    ORDER BY padre, clave 
END 
GO