2009-01-30 90 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