对不起标题,很难解释。
我需要一个与此类似的数据模型:
如您所见,一组可以属于用户或学校。我的问题:它应该只允许属于用户或学校。但绝不是两个在同一时间。
我该如何解决这个问题?
答案 0 :(得分:6)
您当前的设计称为独占弧,其中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,然后才能插入用户或学校。因此,要在用户和学校中使用而不是自动递增;只使用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必须匹配一个或另一个表,用户或学校,但不能同时匹配。
答案 1 :(得分:2)
需要注意的是:数据模型中的Set是什么并不完全清楚。
我正在质疑你的数据模型。
为什么用户 - >集和学校 - >集必须指向同一个表。如果它们是真正独立的数据集,则不会更加规范化。仅仅因为它们在跟踪的列中共享一些相似之处并不意味着它们应该存储在同一个表中。它们在逻辑上是一回事吗?
只需为学校集和用户集创建单独的表。这将使反向查询更容易,因为您不必检查Sets表中的空关系,以了解它是否真的是UserSet或SchoolSet。
答案 2 :(得分:1)
您必须使用触发器来强制执行此业务规则,因为MySQL已经但不强制执行CHECK约束(在任何引擎上)。