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

时间:2016-02-15 03:32:21

标签: database-design foreign-keys

(我有点大脑屁,我不记得这个问题类的名字了,因为我之前已经看过这个问题的解决方案了,所以请把它标记为另一个的重复如果你以前在这里找到更好的答案,请提出问题。)

想象一下,我们有一个汽车和CarOwners数据库。每个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)
)

对于拥有最喜欢的汽车的所有者,需要使用带有外键约束的新列Owners扩展FaveCarId bigint表到Cars表,但添加了所有者和汽车之间的不一致耦合(例如,如果我们将HousesComputers添加到数据库,当我们获得有关所有者的信息时,我们不关心他们的FaveCar是什么。这也增加了鸡和蛋的问题,FaveCar列必须NULL能够创建Owner行,添加新的Car行,然后添加新的CarID返回FaveCar列。

...所以另一种解决方案是扩展Cars列以添加布尔IsFavourite列,但这有一个问题,即没有任何东西阻止某人提供两个Car s(属于同一Owner)集合IsFavourite列值。唯一约束(在OwnerId+IsFavourite之间也无济于事,因为如果所有者拥有3辆或更多车辆,其中2辆将拥有IsFavourite=0

2 个答案:

答案 0 :(得分:1)

关联表解决方案

CREATE TABLE OwnersFavoriteCar (
    OwnerId bigint PRIMARY KEY,
    CarId bigint,
    FOREIGN KEY (OwnerId) REFERENCES Owners(OwnerId),
    FOREIGN KEY (CarId) REFERENCES Cars(CarId)
);

您可以创建通常用于M:N关系的关联表,但将主键设置为OwnerId。这可以防止车主拥有一辆以上的车。

如果您认为自己可以添加HouseComputer,并且除了Car之外,所有者也会收藏一个,您可以将其重命名为{ {1}}并添加两列:

OwnersFavoriteStuff

数据库继承解决方案

或者,您可以继续进行数据库继承:

HouseId bigint -- with FK constraint
ComputerId bigint -- with FK constraint

使用此数据库无效模式,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) ); 是最高级别,ThingsCars都扩展为Houses

您通常会在ThingsBEFORE INSERT上使用Cars触发器,该触发器会先将行插入Houses,获取新的ThingId,并将其插入{ {1}}或Things

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

答案 1 :(得分:1)

尝试以下数据模型:

enter image description here

IsFavorite是OwnedCar的一个属性,由两者 Person和Car识别,而不仅仅由Person或Car识别。 因此需要复合键 您可以将IsFavorite实现为OwnedCar列,也可以创建一个新实体FavoriteCar来增加完整性。

要约束OwnedCar和FavoriteCar,您需要创建唯一索引(备用密钥)。

  • 对于OwnedCar,为CarId添加唯一索引,以便每个Car只能由一个Person拥有。
  • 对于FavoriteCar,为OwnerId添加一个唯一索引,以便每个人只有一个最喜欢的汽车。

没有复合钥匙的问题是,您可以插入所有者不拥有汽车的喜爱汽车。

CREATE TABLE OwnersFavoriteCar (
    OwnerId bigint PRIMARY KEY,
    CarId bigint,
    FOREIGN KEY (OwnerId) REFERENCES Owners(OwnerId),
    FOREIGN KEY (CarId) REFERENCES Cars(CarId)
);