MySQL中的一对多父子表循环关系

MySQL中的一对多父子表循环关系,mysql,sql,parent-child,circular-reference,Mysql,Sql,Parent Child,Circular Reference,我面临着这个问题: 我有一个父表和一个子表,一个父表可以有多个子表,标准故事 这些是制约因素: Parent Table parentId (PK) Child table: childId (PK) parentId (FK) Is Favorite table: childID (PK)(FK) Is Least Favourite table: ChildID (PK)(FK) 每个家长必须至少有一个孩子 每位家长必须有一个最喜欢的孩子 每个家长可以有一个最不喜欢的孩子 如何

我面临着这个问题:

我有一个父表和一个子表,一个父表可以有多个子表,标准故事

这些是制约因素:

Parent Table
parentId (PK)

Child table:
childId  (PK)
parentId (FK)

Is Favorite table:
childID (PK)(FK)

Is Least Favourite table:
ChildID (PK)(FK)
  • 每个家长必须至少有一个孩子
  • 每位家长必须有一个最喜欢的孩子
  • 每个家长可以有一个最不喜欢的孩子
如何在SQL中设计它

由于循环关系,我不确定是否可以使用标准的父子表:

Parent table:
parentId
favouriteChildId NOT NULL
leastFavouriteChildId NULL

Child table:
childId
parentId
我曾考虑使用桥接表,但不确定如何对这些约束进行建模


编辑:为了更清楚,这里是问题背景的一部分:

有价格表(子级)和价格组表(父级)

PriceGroup有多个价格,一个强制主价格(FavoriteChild)和一个官方价格(leastFavouriteChild)

以下内容与该问题无关,但对上下文有所启发:
价格根据所参考的产品进行分组,一个产品可以有多个价格-然后将这些价格分组,每个分组需要参考主价格和官方价格(如果有)。

如果只有一个级别:

Parents ( ParentID, Title, etc )
Children ( ChildID, ParentID, Title, etc )
每个
子项
必须始终恰好有1个
父项
父项
必须始终有
=0个子项
。(这是没有办法的。)

如果有多个(未知)深度:

Items ( ItemID, ParentItemID NULL, Title, etc )
非常简单:
Items.ParentItemID=Items.ItemID

编辑

如果您需要几个(未知)级别的深度,查询将是多个,缓存总结果将是一个非常非常好的主意。(每个子级都会有另一个查询来获取其直接子级等)

如果只有一级深度:

Parents ( ParentID, Title, etc )
Children ( ChildID, ParentID, Title, etc )
每个
子项
必须始终恰好有1个
父项
父项
必须始终有
=0个子项
。(这是没有办法的。)

如果有多个(未知)深度:

Items ( ItemID, ParentItemID NULL, Title, etc )
非常简单:
Items.ParentItemID=Items.ItemID

编辑
如果您需要几个(未知)级别的深度,查询将是多个,缓存总结果将是一个非常非常好的主意。(每个子项都会有另一个查询来获取其直接子项等)。

您可以(在一定程度上)对其他约束进行建模:

Parent Table
parentId (PK)

Child table:
childId  (PK)
parentId (FK)

Is Favorite table:
childID (PK)(FK)

Is Least Favourite table:
ChildID (PK)(FK)
一行将始终插入到最喜爱的子对象中;只有当存在最不喜欢的子对象时,才会在“最不喜欢”中插入一个:插入、更新、删除由视图上的触发器完成;通过表上的左联接进行选择

这不涉及最喜欢的子关系的强制性质-这必须由插入/更新/删除触发器来处理。

您可以(在一定程度上)对其他约束进行建模:

Parent Table
parentId (PK)

Child table:
childId  (PK)
parentId (FK)

Is Favorite table:
childID (PK)(FK)

Is Least Favourite table:
ChildID (PK)(FK)
一行将始终插入到最喜爱的子对象中;只有当存在最不喜欢的子对象时,才会在“最不喜欢”中插入一个:插入、更新、删除由视图上的触发器完成;通过表上的左联接进行选择


这不涉及收藏夹子关系的强制性质-必须由插入/更新/删除触发器处理。

超出您提供的业务规则

  • 每个家长必须至少有一个孩子
  • 每位家长必须有一个最喜欢的孩子
  • 每个家长可以有一个最不喜欢的孩子
  • 你的解决方案

    Parent table:
    parentId (PK)
    favouriteChildId NOT NULL (FK)
    leastFavouriteChildId NULL (FK)
    
    Child table:
    childId (PK)
    parentId (FK)
    
    满足2和3。 但它也满足1(因为FavoriteChildId NOT NULL将不允许创建没有子项的父记录)

    既然您已经有了上述内容,我将假设您真正的问题是如何使子表中的parentId不为NULL

    通常,SQL中有一些规定,以便您可以执行以下操作

    BEGIN TRANS
    INSERT INTO TABLE1 (FK not checked yet)
    INSERT INTO TABLE2 (FK not checked yet)
    COMMIT (All integrity checked)
    
    在这种情况下,“循环引用”不会成为问题(请参阅)

    Mysql不支持它,所以您有以下选项

    触发器:
    可以假设在插入父记录时,已经知道了一个最喜爱的子记录,那么您可以在父表和

    • 将最喜爱的子项插入子表以获取其ID
    • 插入具有子ID的父记录
    注意:问题在于,通过这种方式,您可以正式满足条件,但要首先插入子记录,您必须在父表中使用附加列,以便触发器可以了解子表中的其他字段,或者插入空白记录(在这两种情况下,设计都不是干净的)

    通过安全实现完整性
    上述内容可以作为存储过程实现,而不需要在父表级别上添加其他字段。但是,存储过程通常可以被绕过,因此它不符合真正的完整性规则

    有一种通用方法可以使使用存储过程实现的某些功能符合完整性规则,即删除所有常规用户(和应用程序)对这些表的写入权限,并允许仅通过存储过程更改数据

    编辑: 关于触发器,还有一种使用触发器实现规则的方法,那就是接受您必须单独插入记录,并且您可以在某一时刻拥有违反业务规则的数据

    在这种情况下,您可以拥有父记录的状态属性(例如:COMPLETE vs COMPLETE),并将FavoriteChildId设置为可为空的FK,但在更新状态以完成时,您可以进行触发器检查,以确保完整性得到尊重


    这需要额外的列,但可以使事情变得非常清晰(实际上,您可以在此表上创建一个视图,该视图只会公开完整的记录,有效地使其看起来像带有FK NOT NULL的表)。

    超出您提供的业务规则

  • 每一位家长