如果一个孩子是特殊情况,我如何最好地代表一对多的关系?

tro*_*skn 6 database ruby-on-rails cardinality

我有一个在两个实体之间具有父子关系的模式.它是一对多的,所以自然会被实现为:

table Parents
  id

table Children
  id
  parent_id
Run Code Online (Sandbox Code Playgroud)

但是,我还需要将其中一个孩子视为一个特殊情况(让我们称之为"晋升"的孩子).我可以想到几种方法,包括:

只需输入一个属性Children:

table Parents
  id

table Children
  id
  parent_id
  is_promoted
Run Code Online (Sandbox Code Playgroud)

这显然是有缺陷的,因为数据库不能保证一致性(至少不能通过常规外键约束)

Parents表上创建一个外键:

table Parents
  id
  promoted_child_id

table Children
  id
  parent_id
Run Code Online (Sandbox Code Playgroud)

这是我的第一个倾向,但它确实有一个循环依赖的明显缺点,所以可能不是最优的.

我能想到的另一个选择是在Children桌面上放置第二个外键:

table Parents
  id

table Children
  id
  parent_id
  promoted_parent_id
Run Code Online (Sandbox Code Playgroud)

这允许我放置唯一索引,从而强制数据库一致性.这样做的一个问题是,有可能产生无意义的关系,其中父母A的孩子列为父母B的提升者.

我能想到的最后一个选项是创建一个中间表,例如:

table Parents
  id

table Children
  id

table ParentChildRelationship
  parent_id
  child_id
  is_promoted
Run Code Online (Sandbox Code Playgroud)

同样,我可以在parent_id+ 上声明一个唯一约束is_promoted,强制执行一致性.我对这个问题有点矛盾,因为将关系推广到一个完整的实体似乎有些过分,尽管我把它放在它上面的那一刻(实质上is_promoted就是这个),我想这是有道理的.

我想知道你会考虑用什么方法来处理这个问题.特别是,我正在使用Rails,因此可能会影响最实用的解决方案.

小智 2

我没有任何 Rails 经验,但在处理一对多关系的默认值时遇到过类似的情况,并且通常对数据库模式使用类似的内容:

table Parents
  id

table Children
  id
  parent_id
  FK (parent_id) references Parents(id)

table PromotedChildren
  child_id
  parent_id
  FK (child_id, parent_id) references Children(id, parent_id)
  UNIQUE Key parent_id
Run Code Online (Sandbox Code Playgroud)

编辑

回答这个问题后,我开始思考为什么我更喜欢这种表示而不是其他表示,所以我做了更多的研究并进行了更多的思考:

table Parents
  id

table Children
  id
  parent_id
  is_promoted
Run Code Online (Sandbox Code Playgroud)

如果按照 Kelvin 的建议,如果数据库忽略空值,那么 Jef 提出的对 (parent_id, is_promoted) 的唯一约束将起作用,并且可能是一个不错的选择。然而,有些人可能会认为使用 null 代替 false/0 是 null 的错误用法,它有效地将字段变成三态,即 1、0、未知。此外 (parent_id, is_promoted) 不是超级键,因此违反了域键范式

table Parents
  id
  promoted_child_id

table Children
  id
  parent_id
Run Code Online (Sandbox Code Playgroud)

正如您所指出的,这可能会导致循环,并且通过将promoted_child_id设置为引用Children(id)的FK,您仍然面临数据库异常的风险,其中ParentA将ParentB的孩子列为升级。

table Parents
  id

table Children
  id
  parent_id
  promoted_parent_id
Run Code Online (Sandbox Code Playgroud)

在这里,您会遇到所描述的异常风险;Promotion_parent_id 上的唯一键还必须依赖于数据库忽略空值;多个级联路径;冗余,promoted_pa​​rent_id 和parent_id 应该相同;更新parent_id与promoted_pa​​rent_id不匹配的异常。

table Parents
  id

table Children
  id

table ParentChildRelationship
  parent_id
  child_id
  is_promoted
Run Code Online (Sandbox Code Playgroud)

这看起来更像是父对子的多对多表示。它提出了与上述其他问题类似的问题。

至于什么对于 Rails 开发来说是最实用的,我不能说。我已经使用了我提出的表示法和 Jef 的 C# 和 PHP 表示法,但并没有真正注意到差异。