【问题标题】:Looking for a way to store a parent-child-grandchild structure寻找一种存储父子孙结构的方法
【发布时间】:2011-08-25 17:29:58
【问题描述】:

我需要存储一个结构,其中 N 个父母将有 1 到 N 个孩子,每个孩子将有 1 到 N 个孩子。我想以相对高性能和高度可扩展的方式将其存储在数据库中,而无需更改数据库架构。

每个父母都必须是唯一的,N 个父母可能有同一个孩子。但是,该孩子可能有不同的孩子,具体取决于父母。 (清如泥?)

这可能更容易描述为 parentA 可能有一个具有某些属性(棕色头发,棕色眼睛)的男孩。 parentb 也有一个男孩,但这个孩子有金色的头发和蓝色的眼睛。我需要以标准化的方式存储每个孩子(男性和女性)和每个属性(头发和眼睛的颜色),并以这样的方式关联它们:当我查询父母时,我会得到他们的所有孩子和这些孩子的属性。

我对 SQL 中的树形结构和分层结构做了一些工作,但我很难以符合我对性能和可扩展性要求的方式来概念化这个特定场景。将定期(如果不频繁)添加子属性和相关属性。 提前致谢。我知道需要澄清。

补充说明

好的,看来可能需要一个不同的示例。让我们使用汽车这个古老的例子。

CarA 和 CarB 都有方向盘、发动机和轮胎。 CarA 的方向盘上有无线电控制装置。 CarB 的方向盘没有。 CarA 有一个六缸马达,CarB 有一个八缸马达。我需要使用该功能的属性来模拟每辆车和每个功能之间的关系。我有帮助吗? -rb

【问题讨论】:

    标签: c# sql sql-server sql-server-2008


    【解决方案1】:

    如果这被固定在三层并且它们在概念上是不同的(如在您的扩展示例中),那么我认为您对树的概念感到困惑,因为它们不是必需的。只需像处理任何其他问题一样使用表和关系。

    • 给父母的桌子
    • 孩子的表(如果他们总是有两个父母,父母可以是字段,否则你还需要一个表来表示关系)
    • 一个表用于属性,另一个表用于该表与子级之间的多对多关系 [或将这些存储在子表中 - 请参阅下面 DForck42 的评论]

    在不同级别的节点是“同一事物”的情况下,树是必要的。但它们不太适合 sql,所以我不会尝试在似乎没有必要的地方使用它们。


    update. 从您下面的 cmets 我认为您是说孩子分为类或类型,并且可能的属性取决于孩子的 type ,但这些属性的值取决于父级。

    在这种情况下,您会遇到一个完全不同的问题,更像是 OO 继承。我看到的最简单的解决方案是您可以为每种孩子使用不同的桌子。那么每个表对于不同的属性都有不同的列。子表引用父表。

    所以你会有一个带有 ID 的父表。那么您可能有一个“管理站点”的子表。该子表的每一行都将通过 ID 引用父级,并包含 URL、CSS 等作为列。另一个子类型,如“数据库配置页面”将在另一个表中,具有不同的属性集。

    如果您有共同的属性,那么您可以在每个表中重复它们或拥有一个“超类”表。

    这样的解决方案可能会变得相当复杂,我建议您在对您想要的东西有了更清晰的解释后再问一个问题。这里对选项有很好的解释 - http://www.sqlalchemy.org/docs/orm/inheritance.html(忽略与 SQLAlchemy 相关的部分,只需看看它们如何以不同的方式使用表来建模继承)。

    【讨论】:

    • 如果属性的数量很大或动态,我只会使用 m-m 关系做属性。否则我会把它们放在儿童表中。
    • 好点。我假设是动态的,但问题中并没有真正说明这一点。
    • @andrew:这就是我目前所拥有的。我对这个结构的问题是,给定的孩子将有不同的属性给定与其相关的父母。此外,属性的值肯定会有所不同。假设您是网络主机,clientA 是父级,ClientA 的管理站点是子级,属性可能是 url 和 css。每个父母都可能有一个管理站点,但属性会改变。他们可能需要一个额外的属性,比如加密。
    • @andrew:谢谢,这帮助我克服了障碍。
    【解决方案2】:

    按照我阅读您的问题的方式,您只需要五张表。

     -> Parent
        ParentId, Col1, Col2, Col3
    
     -> Child
        ChildId, Col1, Col2, Col3
    
     -> Grandchild
        GrandchildId, Col1, Col2, Col3
    
     -> ParentToChild
        ParentId, ChildId
    
     -> ChildToGrandchild
        ChildId, GrandchildId
    

    它存储了所有的关系,你可以对你想要的逻辑进行约束;通过此实现,(父母,孩子)和(孩子,孙子)可能有 N 到 N 关系。

    【讨论】:

    • 这与我过去所做的类似。但是,我遇到的问题是 ChildToGrandchild 关系会因父母而异。所以,也许有一个更好的例子。我会更新原帖。
    【解决方案3】:

    嗯,这是另一种方法。你只需要两张桌子。第一个是您存储构成您的层次结构的所有“对象”(无论它们是什么)的地方:

    ObjectID | ObjectName | ...
    

    第二个是关系表:

    RelID | ParentID | ChildID
    

    关系表可以包含一个约束,确保没有一个对象是多个父级的子级,这几乎可以免费为您提供完整性。

    现在遍历表以提取层次结构可能会变得很棘手,但可以使用相对简单的存储过程来完成。有两个陷阱。首先,您的所有对象都应该共享同一个表,因此具有相同的唯一 ID(理想情况下)。其次是您的数据库支持多少级递归。例如,根据我的经验,SQL Server 支持的 32 个级别已经绰绰有余。但是,在代码中而不是在数据库中进行遍历会降低性能。

    还有其他方法可以解决这个问题。如果你用谷歌搜索database hierarchical data,你会找到一些,包括一两篇正式的 CS 论文。

    我过去使用过这种方法,我发现它足够简单且性能良好。

    【讨论】:

      【解决方案4】:

      以下方法有什么问题:

      create Table Persons    {
        PersonID int Primary Key,
        Name  varchar(100),
        MotherID int {Foreign Key},
        FatherID int {Foreign Key}
      }
      
      create Table Attributes
      {
          PersonID int {Foreign Key},
          AttributeName varchar(10),
          AttributeValue varchar(10)
      }
      

      您可以通过以下方式获取给定人员子项的所有属性:

      Select
            Persons.Name,
            Attributes.AttributeName,
            Attributes.AttributeValue
      From
            Persons
      Left Join
            Atttributes
      On
           Persons.PersonID = Attributes.PersonID  
      Where
           MotherID = @PersonID or FatherID = @PersonID
      

      【讨论】:

      • 如果孩子也可以成为父母,那就没问题了。但如果孩子不能成为父母,那么它不会强制执行所有域结构。
      猜你喜欢
      • 2011-08-07
      • 1970-01-01
      • 1970-01-01
      • 2020-09-25
      • 2016-12-10
      • 2011-01-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多