Dai*_*Dai 5 database-design foreign-keys
(我有点脑子放屁,我不记得这个问题类的名称,因为我之前已经在 SO 上看到过这个问题的解决方案,所以请将此标记为另一个问题的重复项,如果您之前在这里找到更好的答案)。
假设我们有一个汽车和车主的数据库。每个 CarOwner 有许多汽车(一对多),但每个 CarOwner 也有一辆最喜欢的汽车。
这是一个初始架构:
CREATE TABLE Owners (
OwnerId bigint IDENTITY(1,1) PRIMARY KEY,
Name nvarchar(100)
)
CREATE TABLE Cars (
CarId bigint IDENTITY(1,1) PRIMARY KEY,
Vin varchar(17),
OwnerId bigint,
FOREIGN KEY (OwnerId) REFERENCES Owners(OwnerId)
)
Run Code Online (Sandbox Code Playgroud)
对于拥有一辆最喜欢的汽车的车主来说,该表需要使用一个带有外键约束的Owners新列来扩展,但会在车主和汽车之间添加不必要的耦合(例如,如果我们将或添加到数据库中,当我们获取有关所有者的信息,我们不关心他们是什么。这也增加了先有鸡还是先有蛋的问题,其中列必须能够创建行、添加新行,然后将新行添加回这FaveCarId bigintCarsHousesComputersFaveCarFaveCarNULLOwnerCarCarIDFaveCar。
...所以另一个解决方案是扩展Cars列以添加布尔IsFavourite列,但这有一个问题,因为没有什么可以阻止某人给两个Cars (属于同一个Owner)设置IsFavourite列值。唯一的约束(之间OwnerId+IsFavourite也无济于事,因为如果车主拥有 3 辆或更多汽车,其中 2 辆将拥有IsFavourite=0.
关联表解决方案
CREATE TABLE OwnersFavoriteCar (
OwnerId bigint PRIMARY KEY,
CarId bigint,
FOREIGN KEY (OwnerId) REFERENCES Owners(OwnerId),
FOREIGN KEY (CarId) REFERENCES Cars(CarId)
);
Run Code Online (Sandbox Code Playgroud)
您可以创建一个通常用于M:N关系的关联表,但将主键设置为OwnerId。这可以防止车主拥有多于一辆最喜欢的汽车。
如果您认为可以添加House或Computer,并且所有者除了 之外还喜欢其中任何一个Car,您可以将其重命名为OwnersFavoriteStuff并添加两列:
HouseId bigint -- with FK constraint
ComputerId bigint -- with FK constraint
Run Code Online (Sandbox Code Playgroud)
数据库继承解决方案
或者,您可以选择数据库继承:
CREATE TABLE Things (
ThingId bigint PRIMARY KEY
ThingType nvarchar(10) CHECK (ThingType in ('car', 'house')) -- type discriminator
);
CREATE TABLE Cars (
ThingId bigInt PRIMARY KEY,
Vin nvarchar(17),
OwnerId bigint,
FOREIGN KEY (OwnerId) REFERENCES Owners(OwnerId),
FOREIGN KEY (ThingId) REFERENCES Things(ThingId)
);
CREATE TABLE Houses (
ThingId bigInt PRIMARY KEY,
Color nvarchar(17),
Rooms bigint,
OwnerId bigint,
FOREIGN KEY (OwnerId) REFERENCES Owners(OwnerId),
FOREIGN KEY (ThingId) REFERENCES Things(ThingId)
);
CREATE TABLE OwnersFavoriteThing (
OwnerId bigint ,
ThingId bigint,
Type nvarchar(10) CHECK (Type in ('Car', 'House')), -- Used to discriminate amoung different Things
PRIMARY KEY (OwnerId, ThingId), -- Makes sure that owner cannot have more than one favorite thing of each type
FOREIGN KEY (OwnerId) REFERENCES Owners(OwnerId),
FOREIGN KEY (ThingId) REFERENCES Things(ThingId)
);
Run Code Online (Sandbox Code Playgroud)
对于此数据库继承模式,Things是顶层,并且Cars和Houses都扩展Things。
您通常会BEFORE INSERT在 on 上使用触发器Cars,Houses该触发器首先会在 中插入一行Things,获取新的 ThingId,然后将其插入到Cars或中Houses。
现在,无论您在数据库中创建多少个新表,您的FavoriteThing 表仍保持第三范式。该type列用于区分不同类型的things。