您当前的设计称为独占弧,其中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;
}
你的cmets:
所以 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 的 Class Table Inheritance 模式。
如果我想获取学校或用户的名称(无论是哪种类型),我可以通过一个查询来完成,还是需要两个查询(第一个获取类型,第二个获取名字)?
您需要加入。如果您从给定的 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,但不能同时匹配。