3

我有一个具有以下结构的菜单系统表和一些数据。

ID、文本、ParentID、DestinationID
1、应用程序,(空),(空)
2、游戏,(空),(空)
3,办公室,1,(空)
4、文本编辑、1、(空)
5,媒体,(空),(空)
6、字、3、1
7、Excel、3、2
8、孤岛危机、2、3

我需要的是一个查询,我可以将菜单 ID 传递给该查询,它将返回具有该 ID 作为子项的项目列表。但是,我需要它只返回具有到达目的地的有效路径的孩子。所以在上面的例子中,用户最初会看到(应用程序,游戏),当他选择应用程序时,他会看到(办公室)。文本编辑和媒体应该被省略,因为它们下面没有有效的目的地。

最棘手的是,任何给定菜单都没有预先确定的深度。

编辑

今天,MS SQL 2008 出现了问题,但在过去的两周里,我需要 SQLite 和 SQL CE 的类似解决方案。理想的解决方案不应与任何特定的 SQL 引擎绑定。

4

8 回答 8

7

仅限 SQL 服务器,但听起来像是Common Table Expressions的工作。

于 2009-01-30T14:06:11.963 回答
3

在甲骨文中:

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首先,使用假parentID的 of0代替NULL.

于 2009-01-30T13:56:57.553 回答
3

If the hierarchy/tree that you are stroing in your database does not change very often, I would recommend to use the modified preorder tree traversal (MPTT) algorithm. That would require a different table schema, but would allow you to request a whole subtree with a simple SQL statement (no recursion, etc.).

The article on Storing Hierarchical Data in a Database describes this method in detail.

In your example you would get the following tree, where I call the red numbers the left value and the green right value of a node.

alt text

Now, if you want to select the Office subtree, you can do this with:

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

If your DB does not support the BETWEEN statement, you can of course write left > 10 AND left < 15 instead.

Your table would look like this:

name         | left | right | destination
------------------------------------------ 
root         | 1    | 17    | NULL
Applications | 7    |  16   |  ...
...
于 2009-02-02T17:25:55.633 回答
1

如果这是您感兴趣(或困扰您)的问题,您可能需要查看:Joe Celko 为 Smarties 编写的 SQL 中的树和层次结构

于 2009-01-30T14:09:27.870 回答
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 依赖于其他 2 个产品。产品 4 依赖于另外 2 个,其中一个依赖于它的部分依赖于另外两个。

此模型有很多冗余,但可以轻松计算特定产品需要多少螺钉,快速确定哪些零件需要木板或获取产品最终依赖的所有组件列表(包括间接依赖关系)等。而扫描某一层以下的树就是一个简单的 LIKE 查询!

通过在十六进制表示中使用 2 个字符,我将产品限制为直接依赖于最多 256 个其他 prod(这反过来又可以依赖于其他东西)。如果您需要更多,您可以将其更改为使用 base 36(26 个字母加 10 个数字)或 base-64。

此外,这个表模型在 Access 和 mySQL 上也能很好地工作。你不能拥有的是任何方式的循环依赖。

于 2009-01-31T06:12:52.997 回答
0

SQL 不太擅长遍历任意深度的层次结构。

如果这些记录少于 1000 条,我会将它们全部抓取到应用程序中并在那里构建图表。

如果这些记录超过 1000 条,我会将它们分组为大约 1000 个的原始子树(通过添加 SubtreeID 外键)并获取每个子树......然后在应用程序中构建子树的图形。

于 2009-01-30T14:16:15.783 回答
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) 作为它的参数,并且每次后续单击都会动态地按顺序保存下一个参数(这似乎匹配你的评论)

例如

单击顶级菜单:- 值为(空)

单击应用程序:- 值为 1

单击办公室:- 值为 3

等等

假设 destinationID 除了显示一个活动的子链接(允许您删除它)之外什么都不做,那么代码将如下所示:

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 设置为父 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)
于 2009-01-30T21:43:58.670 回答
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
于 2017-06-29T14:27:16.130 回答