代码之家  ›  专栏  ›  技术社区  ›  George

一对多关系的约束

  •  4
  • George  · 技术社区  · 16 年前

    我们有一对多关系的两张桌子。我们希望强制一个约束,即对于给定的父记录,至少存在一个子记录。

    这有可能吗?

    如果没有,您会改变模式更复杂一点来支持这样的约束吗?如果是,你会怎么做?

    编辑:我正在使用SQL Server 2005

    5 回复  |  直到 9 年前
        1
  •  7
  •   Adam Robinson    16 年前

    从模式的角度来看,这样的约束是不可能的,因为您遇到了“鸡或蛋”类型的场景。在这种情况下,当我插入父表时,必须在子表中有一行,但是在父表中没有行之前,不能在子表中有行。

    这是一个更好的强制客户端。

        2
  •  1
  •   Kev    16 年前

    如果您的后端支持可推迟的约束,这是可能的,PostgreSQL也是如此。

        3
  •  0
  •   Vincent Buck    16 年前

    简单的不可以为空的列怎么样?

    Create Table ParentTable
    (
    ParentID
    ChildID not null,
    Primary Key (ParentID), 
    Foreign Key (ChildID ) references Childtable (ChildID));
    )
    

    如果您的业务逻辑允许并且您有默认值,那么可以从数据库中为每个新的父记录查询,然后可以使用 before insert trigger 在父表上填充不可为空的子列。

    CREATE or REPLACE TRIGGER trigger_name
    BEFORE INSERT
        ON ParentTable
        FOR EACH ROW 
    BEGIN
    
        -- ( insert new row into ChildTable )
        -- update childID column in ParentTable 
    
    END;
    
        4
  •  0
  •   bielawski    10 年前

    这并不是“在客户机端更好地执行”的功能,而是在某些数据库实现中无法执行的功能。实际上,该作业确实属于数据库,下面的至少一个解决方法应该有效。

    最终,您想要的是将父对象约束到子对象。这就保证了孩子的存在。不幸的是,这会导致鸡蛋问题,因为孩子们必须指向同一个家长,从而导致约束冲突。

    在系统的其他部分,如果没有明显的副作用,解决这个问题需要两种能力中的一种——在SQL Server中找不到这两种能力。

    1)延迟约束验证-这将导致在事务结束时验证约束。通常它们发生在语句的末尾。这是鸡蛋问题的根源,因为它阻止您插入第一个子行或父行,因为缺少另一个子行,这就解决了这个问题。

    2)可以使用CTE插入第一个子级,其中CTE挂起插入父级的语句(或相反)。这将在同一语句中插入两行,导致类似于延迟约束验证的效果。

    3)如果没有任何一个引用,则只能在其中一个引用中允许空值,这样就可以在不进行依赖性检查的情况下插入该行。然后您必须返回并使用对第二行的引用更新空值。如果使用此技术,则需要小心,使系统的其余部分通过隐藏子引用列中所有空行的视图引用父表。

    在任何情况下,您的删除 子级的操作也同样复杂,因为您无法删除证明至少存在一个子级的子级,除非您首先更新父级以指向不会被删除的子级。

    当您要删除最后一个子项时,您必须抛出一个错误或同时删除父项。如果不先将父指针设置为空(或推迟验证),则会自动发生错误。如果延迟(或将子指针设置为空),则可以删除子对象,然后也可以删除父对象。

    我确实研究了很多年,并且观察了每一个版本的SQL Server以缓解这个问题,因为它是如此常见。

    拜托 一旦有人有切实可行的解决方案,请发帖!

    另外,在从父行引用子行的证明时,您需要使用复合键,或者使用触发器来确保提供证明的子行实际上将该行视为其父行。

    p.p.s虽然如果在同一事务中同时执行插入和更新操作,系统的其余部分永远不会看到空值,但这取决于可能失败的行为。约束的要点是确保逻辑故障不会使数据库处于无效状态。通过使用隐藏空值的视图保护表,任何非法行都将不可见。显然,您的插入逻辑必须考虑到这样一行可能存在的可能性,但无论如何它都需要内部知识,而其他什么都不需要知道。

        5
  •  0
  •   JasonInVegas    9 年前

    我遇到了这个问题,并在OracleRel.11.2.4中实现了一个解决方案。

    1. 为了确保每个子代都有一个父代,我将一个典型的外键约束从子代的fk应用到父代的pk。--这里没有巫师
    2. 为了确保每个家长至少有一个孩子,我做了如下操作:

    创建一个接受父pk的函数,并返回该pk的子级计数。--我确保没有发现异常的数据返回0。

    创建虚拟列 CHILD_COUNT 在父表上,并将其计算为函数结果。

    在上创建可延迟的检查约束 儿童计数 条件为的虚拟列 CHILD_COUNT > 0

    其工作原理如下:

    • 如果插入了父行,但还不存在子行。如果该行已提交,则 儿童计数>0 CHECK约束失败,事务回滚。
    • 如果插入了父行,并且在同一事务中插入了相应的子行,则当 COMMIT 发行。
    • 如果插入了对应于现有父行的子行,则 儿童计数 虚拟列在上重新计算 提交 没有违反完整性。
    • 父行的任何删除都必须级联到子行,否则提交删除事务时,孤立的子行将违反外键约束。(和往常一样)
    • 子行的任何删除操作必须为每个父行至少保留一个子行,否则 儿童计数 当事务提交时,CHECK约束将被违反。

    注意:如果Oracle允许基于用户函数的 CHECK constraints 在11月2日。