在 1-to-n 数据库关系中存储首选条目
storing preferred entry in a 1-to-n database relation
我正在研究如何(最好)为关系数据库建模,以便它可以标记 1:n 关系中的(0 或 1)个条目是首选。
(实际上是要在mariadb 10.2中实现,不过应该没那么重要)
为简单起见,假设我有两个 table:
parent:
- id
- 姓名
child:
- id
- 姓名
- parentid (FK -> parent(id) )
问题是我需要建模一种方法来存储首选 child
我看到了两种方式,但我都不太喜欢:
- 在 parent 中添加一个字段,列出首选 child 的 ID,并将其设为 child.
的 FK
- 在 child 中添加一个布尔列以指示行是首选 child。
我看到的东西:
只有 1 个首选 child:
在解决方案 1 的情况下,我只能有一个最大值。首选 child (=good)
在解决方案 2 的情况下,我需要依赖应用程序根据 parent (=bad)
[= 只选择一个 child 79=]
-> 这将我推向解决方案 1
可维护性
解决方案 1 在 table 和 parent 和 child 之间的两个方向上创建 FK。我不确定这样的结构如何能够持续到例如。一个 backup/restore 循环(如果需要的话),因为没有更多的顺序来创建 tables,它们需要在没有 FKs 的情况下创建,然后需要在以后添加。即使这些都被工具覆盖了,我仍然很担心长期的可维护性。
-> 这将我推向解决方案 2
因此,在我们选择一个解决方案之前,我不认为这两个解决方案都不是一个好的解决方案,有人可以考虑其他解决方案吗?
还有什么我忽略的要考虑的吗?
我不确定如何考虑规范化。
编辑:
输入后进一步挖掘,我想到了第三个似乎更优越的选项:添加第三个 table 来模拟首选 child。
本质上:
首选child:
- parentid
- childid
都带有 FK,例如
外键 (parentid, childid) 引用 child (parentid, id)
可能还有一些更独特的键来确保它都是唯一的,但这解决了我上面的解决方案 1 中的 chicken-egg 并避免了应用程序可能在解决方案 2 中造成的混乱。
对于第三个选项,我做了一个快速的 fiddle:http://sqlfiddle.com/#!9/af77bf/5/0
我会选择选项 (2)。
在 PostgreSQL,AFAIK 中,没有 partial UNIQUE constraint
,但有 partial UNIQUE index
,可用于确保具有相同 parentId
的多行只有 1 preferred
.
假设列 preferred
的类型为 boolean
。您可以在列 parentid + preferred
上创建 partial UNIQUE index
,其中 preferred
为真。
CREATE UNIQUE INDEX unique_parentid_preferred ON child (parentid, preferred) WHERE preferred is true;
添加具有相同 parentid
和 preferred = true
的行时抛出此错误:
ERROR: duplicate key value violates unique constraint "unique_parentid_preferred" ...
选项 1 不错,但需要可空的首选 child_id 或临时禁用 FK 检查才能填充。尽管如此,在查询首选 child.
时,它仍然有效地使用了正常的 PK 和 FK 索引
选项 2 不是 IMO 的好解决方案,因为首选 child 标志会在行之间创建依赖关系。更新首选 child 需要更新多行,从而造成不一致的机会。它实际上可以在 MySQL / MariaDB 中以某种方式处理 - 如果您愿意将 NULL 用于 FALSE,(parent_id, is_preferred)
上的唯一索引将只允许一行 is_preferred = TRUE
,并且is_preferred = NULL
的任意行数。但是你必须处理 NULL,这会增加一点复杂性,并且存在 non-NULL non-TRUE 值的风险。
选项 3 不错。它很简单,避免了使其他选项复杂化的问题,同时只要您将 PK / FK 字段编入索引,就可以有效使用索引。
我正在研究如何(最好)为关系数据库建模,以便它可以标记 1:n 关系中的(0 或 1)个条目是首选。
(实际上是要在mariadb 10.2中实现,不过应该没那么重要)
为简单起见,假设我有两个 table:
parent:
- id
- 姓名
child:
- id
- 姓名
- parentid (FK -> parent(id) )
问题是我需要建模一种方法来存储首选 child
我看到了两种方式,但我都不太喜欢:
- 在 parent 中添加一个字段,列出首选 child 的 ID,并将其设为 child. 的 FK
- 在 child 中添加一个布尔列以指示行是首选 child。
我看到的东西:
只有 1 个首选 child:
在解决方案 1 的情况下,我只能有一个最大值。首选 child (=good)
在解决方案 2 的情况下,我需要依赖应用程序根据 parent (=bad)
[= 只选择一个 child 79=]
-> 这将我推向解决方案 1
可维护性
解决方案 1 在 table 和 parent 和 child 之间的两个方向上创建 FK。我不确定这样的结构如何能够持续到例如。一个 backup/restore 循环(如果需要的话),因为没有更多的顺序来创建 tables,它们需要在没有 FKs 的情况下创建,然后需要在以后添加。即使这些都被工具覆盖了,我仍然很担心长期的可维护性。
-> 这将我推向解决方案 2
因此,在我们选择一个解决方案之前,我不认为这两个解决方案都不是一个好的解决方案,有人可以考虑其他解决方案吗?
还有什么我忽略的要考虑的吗?
我不确定如何考虑规范化。
编辑:
输入后进一步挖掘,我想到了第三个似乎更优越的选项:添加第三个 table 来模拟首选 child。
本质上:
首选child:
- parentid
- childid
都带有 FK,例如
外键 (parentid, childid) 引用 child (parentid, id)
可能还有一些更独特的键来确保它都是唯一的,但这解决了我上面的解决方案 1 中的 chicken-egg 并避免了应用程序可能在解决方案 2 中造成的混乱。
对于第三个选项,我做了一个快速的 fiddle:http://sqlfiddle.com/#!9/af77bf/5/0
我会选择选项 (2)。
在 PostgreSQL,AFAIK 中,没有 partial UNIQUE constraint
,但有 partial UNIQUE index
,可用于确保具有相同 parentId
的多行只有 1 preferred
.
假设列 preferred
的类型为 boolean
。您可以在列 parentid + preferred
上创建 partial UNIQUE index
,其中 preferred
为真。
CREATE UNIQUE INDEX unique_parentid_preferred ON child (parentid, preferred) WHERE preferred is true;
添加具有相同 parentid
和 preferred = true
的行时抛出此错误:
ERROR: duplicate key value violates unique constraint "unique_parentid_preferred" ...
选项 1 不错,但需要可空的首选 child_id 或临时禁用 FK 检查才能填充。尽管如此,在查询首选 child.
时,它仍然有效地使用了正常的 PK 和 FK 索引选项 2 不是 IMO 的好解决方案,因为首选 child 标志会在行之间创建依赖关系。更新首选 child 需要更新多行,从而造成不一致的机会。它实际上可以在 MySQL / MariaDB 中以某种方式处理 - 如果您愿意将 NULL 用于 FALSE,(parent_id, is_preferred)
上的唯一索引将只允许一行 is_preferred = TRUE
,并且is_preferred = NULL
的任意行数。但是你必须处理 NULL,这会增加一点复杂性,并且存在 non-NULL non-TRUE 值的风险。
选项 3 不错。它很简单,避免了使其他选项复杂化的问题,同时只要您将 PK / FK 字段编入索引,就可以有效使用索引。