在你给出的业务规则之外
- 每位父母必须至少有一个孩子,
- 每个父母都必须有一个最喜欢的孩子,
- 每个父母都可以有一个最不喜欢的孩子
您的解决方案
Parent table:
parentId (PK)
favouriteChildId NOT NULL (FK)
leastFavouriteChildId NULL (FK)
Child table:
childId (PK)
parentId (FK)
满足 2 和 3。但它也满足 1(因为 favouriteChildId NOT NULL 将不允许创建没有子项的父记录)。
由于您已经拥有上述内容,我将假设您的真正问题是如何使 Child 表中的 parentId 不为 NULL。
通常,SQL 中有规定,因此您可以执行类似的操作
BEGIN TRANS
INSERT INTO TABLE1 (FK not checked yet)
INSERT INTO TABLE2 (FK not checked yet)
COMMIT (All integrity checked)
在这种情况下,“循环引用”不会有问题(参见DEFERRED)
Mysql 不支持它,所以你有以下选项
触发器:
可以假设在插入父记录时已经知道最喜欢的子记录,那么您可以在父表上插入之前运行一个触发器,并且
- 将最喜欢的孩子插入子表中获取其 ID
- 插入带有子 ID 的父记录
注意:问题是这种方式可以正式满足条件,但是要首先插入子记录,您必须在父表中使用其他列,以便触发器可以了解子表中的其他字段或插入空白记录(在无论哪种情况,设计都不干净)
通过安全实现完整性
以上可以作为存储过程实现,而不需要父表级别的附加字段。但是,通常可以绕过存储过程,因此它不符合真正的完整性规则。
有一种通用的方法可以使存储过程实现的目标符合完整性规则 - 即删除所有常规用户(和应用程序)对这些表的写权限,并允许仅通过存储过程更改数据。
编辑:关于触发器,还有一种方法可以使用触发器来实现规则,那就是接受您必须单独插入记录并且您必须拥有的数据,这会破坏您的业务规则。
在这种情况下,您可以为父记录设置一个 STATUS 属性(例如:COMPLETE 与 INCOMPLETE)并将 favouriteChildId 设置为 NULLable FK,但是当将状态更新为 COMPLETE 时,您可以使用触发器检查是否尊重完整性。
这需要额外的列,但可以使事情变得非常干净(您实际上可以在此表上创建一个视图,该视图仅公开已完成的记录,有效地使其看起来像具有 FK NOT NULL 的表)。