【问题标题】:Storing & Querying Heirarchical Data with Multiple Parent Nodes存储和查询具有多个父节点的分层数据
【发布时间】:2011-04-04 18:59:43
【问题描述】:

我一直在进行大量搜索,但未能找到有关该主题的很多资源。我的目标是像在甘特图中那样存储调度数据。因此,存储数据的一个示例可能是:

Task Id | Name    | Duration
1         Task A    1
2         Task B    3
3         Task C    2

Task Id | Predecessors
1         Null
2         Null
3         1
3         2

这将使任务 C 等待任务 A 和任务 B 完成。

所以我的问题是:存储此类数据并有效查询的最佳方式是什么?这种东西有什么好的资源吗?有大量关于树结构的信息,但是一旦添加了多个父节点,就很难找到信息。顺便说一句,我正在使用 SQL Server 和 .NET 来完成这项任务。

【问题讨论】:

  • 虽然我在 MSSQL 2019 中有类似的要求,但已经很长时间了,有什么答案吗?尝试建模有向无环图。本质上是一个有多个父节点的节点,子节点不能是它自己的父节点!!

标签: sql database database-design data-structures


【解决方案1】:

使用邻接表模型:

chain

task_id  predecessor
3        1
3        2

这个查询可以找到给定任务的所有前辈:

WITH    q AS
        (
        SELECT  predecessor
        FROM    chain
        WHERE   task_id = 3
        UNION ALL
        SELECT  c.predecessor
        FROM    q
        JOIN    chain c
        ON      c.task_id = q.predecessor
        )
SELECT  *
FROM    q

获取每个任务最长父级的持续时间:

WITH    q AS
        (
        SELECT  task_id, duration
        FROM    tasks
        UNION ALL
        SELECT  t.task_id, t.duration
        FROM    q
        JOIN    chain с
        ON      c.task_id = q.task_id
        JOIN    tasks t
        ON      t.task_id = c.predecessor
        )
SELECT  task_id, MAX(duration)
FROM    q

【讨论】:

  • 也许我应该更具体一些。我研究了邻接列表模型,但我还没有找到很好的方法来做一些事情,比如汇总我所有任务的持续时间。如果他们没有多个父母,这很容易,但我如何有效地解释我不想要父母双方的总和,而是他们所有人中最长的持续时间这一事实?
  • @BPotocki:请发布一些示例数据和您想要获得的结果集。
【解决方案2】:

您的问题与关系基数的概念有关。所有关系都有一些基数,它表示关系每一侧的潜在实例数,它们是它的成员,或者可以参与关系的单个实例。举个例子,对于人来说,(我猜对大多数生物来说,除了极少数例外),父子关系的基数为2 to zero or many,这意味着它需要两个父母在父母一方,并且可以是零或许多孩子(也许应该是2 to 1 or many

在数据库设计中,一般来说,任何一侧有 1(一)(或零或一)的东西都可以很容易地用两张表来表示,每个实体一张,(有时只需要一张表请参阅注释**)和表中表示“多”端的外键列,它指向另一个表,该表在“一”端保存实体。

在您的情况下,您有 many to many 关系。 (一个任务可以有多个前任,每个前任当然可以是多个任务的前任)在这种情况下,需要第三张表,其中每一行有效地表示两个任务之间的关联,表示一个是任务的前任其他。通常,该表设计为只包含两个父表的主键的所有列,它自己的主键是两个父主键中所有列的组合。在您的情况下,它只有两列,taskId 和 PredecessorTaskId,并且这对 Id 在表中应该是唯一的,因此它们一起形成复合 PK。

查询时,为避免在有多个连接时重复计算父表中的数据列,只需将查询基于父表...例如,查找最长父表的持续时间, 假设您的关联表名为 TaskPredecessor

  Select TaskId, Max(P.Duration)
  From Task T Join Task P
     On P.TaskId In (Select PredecessorId 
                     From TaskPredecessor
                     Where TaskId = T.TaskId)

** 注意。如果关系中的两个实体属于相同的实体类型,它们可以都在同一个表中。典型(luv that word)示例是一个员工表,其中包含 Worker 与 Supervisor 的多对一关系......由于主管也是员工,因此工作人员和主管都可以在同一个 [Employee] 表中,并且关系可以使用指向同一个表中的另一行并包含该员工主管的员工记录的 Id 的外键(称为 SupervisorId)进行建模。

【讨论】:

    【解决方案3】:

    查看“SQL 设计模式”一书中的“分层加权总计”模式,或“SQL 中的树和层次结构”中的“物料清单”部分。

    总之,图具有双重聚合。您沿着每条路径中的节点进行一种聚合,并在替代路径上进行另一种聚合。例如,找到两个节点之间的最小距离是求和最小的。分层加权总查询(又名物料清单)是沿每条路径的数量相乘,并沿每条替代路径求和:

    与 TCAssembly 为 ( 选择零件、子零件、数量 AS factoredQuantity 来自 AssemblyEdges 其中Part =“自行车” 联合所有 选择 te.Part、e.SubPart、e.Quantity * te.factoredQuantity 来自 TCAssembly te,AssemblyEdges e 其中 te.SubPart = e.Part ) 从 TCAssembly 中选择 SubPart, sum(Quantity) 按子部分分组

    【讨论】:

    • 能否请您提供您提到的书的作者?
    猜你喜欢
    • 1970-01-01
    • 2022-01-09
    • 2011-08-10
    • 1970-01-01
    • 2013-08-11
    • 1970-01-01
    • 1970-01-01
    • 2021-06-15
    • 1970-01-01
    相关资源
    最近更新 更多