有没有一般规则或最佳做法的外键不应该为空。很多时候,一个实体不与另一个实体有关系是非常合理的。例如,您可能有一张您正在追踪的艺术家的表格,但此刻,您没有这些艺术家记录的CD。对于可以是音乐/音频或软件的媒体(CD,DVD,BluRay),您可以拥有一个共同信息表,然后是两个外键,每个扩展表一个(AudioData和SoftwareData ),但必须是NULL
。这提出了一种称为除了别的之外的独占弧的情况。 这个通常被认为是有问题的。
想象一个超类和两个派生类,如Java或C++的OO语言。表示,在一个关系模式的一种方法是:
create table Media(
ID int not null, -- identity, auto_generated, generated always as identity...
Type char(1) not null,
Format char(1) not null,
... <other common data>,
constraint PK_Media primary key(ID),
constraint FK_Media_Type foreign key(Type)
references MediaTypes(ID), -- A-A/V, S-Software, G-Game
constraint FK_Media_Format foreign key(Format)
references MediaFormats(ID) -- C-CD, D-DVD, B-BluRay, etc.
);
create unique index UQ_Media_ID_Type(ID, Type) on Media;
create table AVData(-- For music and video
ID int not null,
Type char(1) not null,
... <audio-only data>,
constraint PK_AVData primary key(ID),
constraint CK_AVData_Type check(Type = 'A',
constraint FK_AVData_Media foreign key(ID, Type)
references Media(ID, Type)
);
create table SWData(-- For software, data
ID int not null,
Type char(1) not null,
... <software-only data>,
constraint PK_SWData primary key(ID),
constraint CK_SWData_Type check(Type = 'S',
constraint FK_SWData_Media foreign key(ID, Type)
references Media(ID, Type)
);
create table GameData(-- For games
ID int not null,
Type char(1) not null,
... <game-only data>,
constraint PK_GameData primary key(ID),
constraint CK_GameData_Type check(Type = 'G',
constraint FK_GameData_Media foreign key(ID, Type)
references Media(ID, Type)
);
现在,如果你正在寻找一个电影,你搜索AVData表,然后与媒体表连接为其余的信息等软件或游戏。如果您有一个ID值,但不知道它是什么类型,请搜索媒体表格,Type值会告诉您要连接三个(或更多)数据表中的哪一个。关键是FK是指到的泛型表,而不是它。
当然,电影或游戏或软件可以在多种媒体类型上发布,因此您可以在Media
表和各个数据表之间具有交集表。 Otoh,那些通常贴有不同的SKU标签,因此您可能还想将它们作为不同的项目对待。
正如您所期望的那样,代码可能会变得相当复杂,但不是太糟糕。Otoh,我们的设计目标不是简单的代码,而是数据的完整性。这使得不可能将游戏数据与电影项目混合。并且你摆脱了只有一个必须有一个值而其他必须为空的一组字段。
MySQL不强制执行CHECK约束。 –
然后使用触发器。 – TommCatt
非常感谢,由于截止日期的原因,我不得不放弃一个愚蠢的解决方案,但这对未来将有所帮助。 –