【问题标题】:Return all nodes in many-to-many hierarchal tree返回多对多层次树中的所有节点
【发布时间】:2011-03-08 19:32:00
【问题描述】:

类似于这个问题: How do I query for all the nodes between two nodes in a tree?

但是我没有有一个闭包(扁平)表,一个孩子可以有很多父母,ID遍历不一定是有序的。嵌套深度没有限制。

假设循环引用是不可能的... 我想返回遍历层次结构所需的所有行。

假设如下表:

ParentID    ID    RowNumber(Reference)
1           2     1
2           4     2
4           3     3
3           5     4
1           6     5
6           7     6
2           8     7
3           9     8
1           8     9
6           8     10

鉴于1,我将如何编写一个查询来返回所有行(获取所有后代的关系)?

同样,给定 2 我希望第 2、3、4、7、8 行

鉴于 6 我希望第 6 行和第 10 行

偶尔的误报是可以接受的,因为结果中有重复的行。缺少行是不可接受的

在 MSAccess 和 SQL Server 2000+ 中实现

【问题讨论】:

  • 澄清一下,你的意思是一个孩子可以有很多父母,而不是很多祖先,对吧?我之所以问,是因为您发布的示例数据没有显示任何有多个父母的孩子,尽管 4、3、5、7 有多个祖先。
  • 一个孩子可以有许多父母许多后代。我将添加更多示例
  • 另外,您希望基于示例数据获得的示例查询结果将非常有帮助。请务必使用包含所有可能复杂性的样本数据(例如,有多个父母的孩子等)。
  • 最大深度是多少,如果有的话?您不能在查询 (SQL Server 2000)、存储过程中执行此操作 - 也许
  • @Richard: "嵌套深度没有限制"

标签: sql sql-server ms-access many-to-many hierarchical-data


【解决方案1】:

对于 SQL Server:Adjacency list vs. nested sets: SQL Server

对于 Jet/MS Access,递归查询不是一个选项,因此nested sets 将是可行的方法。示例:http://www.mvps.org/access/queries/qry0023.htm

嵌套集的一些背景知识:

要实现嵌套集解决方案,您需要在表中添加和维护两个额外的列:LtRt(分别为左和右)。您可以通过执行修改后的前序树遍历来填充这些列,以将值分配给这些列。这可以通过递归函数最容易地完成。然后,您可以在 SELECT 时使用 left 和 right 值来确定后代。

折衷方案是在数据更改时需要更多处理,但在检索数据时执行速度要快得多。

这个概念有点不直观,当然有一个学习曲线,但我个人使用它效果很好。据我所知,这是在 Jet(MS Access 数据库引擎)中仅使用 SELECT 查询后完成您所要做的事情的唯一方法。

示例嵌套集解决方案:

ParentID    ID  Lt  Rt  RowNumber(Reference)
Null        1    1  18  0
1           2    2  13  1
2           4    3  10  2
4           3    4   9  3
3           5    5   6  4
1           6   14  17  5
6           7   15  16  6
2           8   11  12  7
3           9    7   8  8

然后获取ID为2的所有后代:

SELECT * FROM Tbl WHERE Lt Between 2 And 13

这是树的图形外观:

【讨论】:

  • 经过仔细检查,似乎递归查询仅在 SQL Server 2005+ 中可用。
  • 请注意,第一个链接还包括 SQL Server 中嵌套集的实现,该实现可在 SQL Server 2000 中运行。该链接中的邻接列表示例仅适用于 SQL Server 2005+。跨度>
  • 我还在纠结如何在允许多个父母的情况下分配 Lt/Rt 数据。
  • 同级物品的左右顺序无关。例如,如果您切换 2 和 6,则 2 最终会得到 diff lt/rt 值。但是,您只需在查询中使用这些值代替 2/13,您仍然会得到 2 的所有后代。
  • 你说得对……对不起,我不够清楚。我确实在我的原始帖子中指定了多个父级(这超出了嵌套集模型的范围。尽管如此,我真的很喜欢这种方法,但我认为它不适用于这里。
【解决方案2】:

由于您需要对节点可以有多个父节点的数据进行建模,因此嵌套集/MPTT 解决方案将不起作用。另一种选择是使用closure table

您将创建一个额外的表,其中包含每个祖先的后代的项目对(反之亦然):

AncID DesID 1 2 1 6 1 4 1 8 1 7 1 3 1 5 1 9 2 4 2 8 2 3 2 5 2 9 4 3 4 5 4 9 3 5 3 9 6 7

然后您将使用连接来获取您需要的项目:

SELECT * 
FROM Tbl INNER JOIN Closure ON Tbl.ID=Closure.DesID 
WHERE Closure.AncID = 2

【讨论】:

  • 我熟悉这种方法(并且在我在我的 OP 中链接的问题中使用了它)。这将需要额外的数据写入处理,这是不可取的。尽管我开始相信这是在单个查询中获取结果的唯一可行方法。请注意,在我的问题中,我将其称为“扁平”表,而不是您使用的公认名称。
  • 除非您使用的是对邻接列表模型(Oracle、SQL Server 2005+)有专有支持的数据库引擎,否则您将需要在写入时或读取时进行额外的处理。如果您想避免管理其他信息,您可以在读取时循环遍历记录集。这将比使用查询慢,但需要较少的维护。这是一个权衡。这取决于您的具体要求。
  • 虽然我不会实现传递闭包表,但我将其标记为答案,因为它是一个可行的解决方案(只是不是我计划使用的解决方案)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-02-03
  • 2015-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-17
  • 1970-01-01
相关资源
最近更新 更多