与“默认”子实例的一对多关系

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.

Mat*_*sen 2

关联表解决方案

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。这可以防止车主拥有多于一辆最喜欢的汽车。

如果您认为可以添加HouseComputer,并且所有者除了 之外还喜欢其中任何一个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是顶层,并且CarsHouses都扩展Things

您通常会BEFORE INSERT在 on 上使用触发器CarsHouses该触发器首先会在 中插入一行Things,获取新的 ThingId,然后将其插入到Cars或中Houses

现在,无论您在数据库中创建多少个新表,您的FavoriteThing 表仍保持第三范式。该type列用于区分不同类型的things