6

对不起标题,很难解释。

我需要一个类似这样的数据模型: 替代文字

如您所见,一个集合既可以属于用户,也可以属于学校。我的问题:它应该只允许属于用户或学校。但绝不会同时发生。

我怎么解决这个问题?

4

3 回答 3

8

您当前的设计称为独占弧,其中sets表有两个外键,并且恰好需要其中一个为非空。这是实现多态关联的一种方法,因为给定的外键只能引用一个目标表。

另一种解决方案是制作一个共同的“超级表”,同时users引用schools和 ,然后将其用作sets.

create table set_owner

create table users 
  PK is also FK --> set_owner

create table schools
  PK is also FK --> set_owner

create table sets 
  FK --> set_owner

您可以将其视为类似于OO 建模中的接口:

interface SetOwner { ... }

class User implements SetOwner { ... }

class School implements SetOwner { ... }

class Set {
  SetOwner owner;
}

回复您的评论:

所以 SetOwner 表同时包含 UserID 和 SchoolID,对吗?这意味着我不会被允许为用户和学校拥有相同的 ID。我该如何执行?

让 SetOwners 表生成 id 值。您必须先插入 SetOwners,然后才能插入用户或学校。因此,使用户和学校中的 id自动递增;只需使用 SetOwners 生成的值:

INSERT INTO SetOwners DEFAULT VALUES; -- generates an id
INSERT INTO Schools (id, name, location) VALUES (LAST_INSERT_ID(), 'name', 'location');

这样一来,学校和用户都不会使用给定的 id 值。

如果我想获取集合的所有者类型,是否需要在我的 SetOwner 表中添加 ownerType 属性?

你当然可以做到这一点。事实上,可能还有其他列对用户和学校都是通用的,您可以将这些列放在超表 SetOwners 中。这进入了 Martin Fowler 的类表继承模式。

如果我想获取学校或用户的名称(无论是哪种类型),我可以用一个查询来完成,还是需要两个查询(第一个获取类型,第二个获取名称)?

你需要加入。如果您从给定的 Set 查询并且您知道它属于用户(不是学校),您可以跳过加入 SetOwners 并直接加入用户。连接不一定要通过外键。

SELECT u.name FROM Sets s JOIN Users u ON s.SetOwner_id = u.id WHERE ...

如果您不知道给定集合属于用户还是学校,则必须对两者进行外部联接:

SELECT COALESCE(u.name, sc.name) AS name 
FROM Sets s 
LEFT OUTER JOIN Users u ON s.SetOwner_id = u.id
LEFT OUTER JOIN Schools sc ON s.SetOwner_id = sc.id 
WHERE ...

您知道 SetOwner_id 必须匹配一个或另一个表,Users 或 Schools,但不能同时匹配两者。

于 2010-06-30T00:19:44.380 回答
2

需要注意的是:在您的数据模型中,Set 是什么并不完全清楚。

我在质疑你的数据模型。

为什么 User -> Sets 和 School -> Sets 必须指向同一张表。如果这些是真正独立的数据集,则不会更加规范化。仅仅因为它们在跟踪的列中有一些相似之处并不意味着它们应该存储在同一个表中。它们在逻辑上是一样的吗?

只需为学校集和用户集创建单独的表。这也将使反向查询更容易,因为您不必检查 Sets 表中的空关系来知道它是真正的 UserSet 还是 SchoolSet。

于 2010-06-29T22:06:55.847 回答
1

您必须使用触发器来强制执行此业务规则,因为 MySQL 有但不强制执行 CHECK 约束(在任何引擎上)。

于 2010-06-29T22:05:41.873 回答