复合主键中的 NULL - SQL Server

Dan*_*Dan 5 sql-server entity-framework

我正在尝试找出创建 SQL Server 主键、外键和约束的最佳方法,以在 LINQ/实体数据对象中准确表示我的数据模型。让我们假设 - 为简化起见 - 我有四个主要表 - Cats、Dogs、Pets 和 Owners - 以及一个关联表:OwnersToPets:

create table Cats (
    ID_Cats int NOT NULL IDENTITY PRIMARY KEY,
    Name nvarchar(50) NOT NULL,
    miceEaten int NULL
)

create table Dogs (
    ID_Dogs int NOT NULL IDENTITY PRIMARY KEY,
    Name nvarchar(50) NOT NULL,
    favouriteToy nvarchar(50) NULL
)

create table Owners (
    ID_Owners int NOT NULL IDENTITY PRIMARY KEY,
    Name nvarchar(50) NOT NULL,
)

create table Pets (
    ID_Pets int NOT NULL IDENTITY PRIMARY KEY,
    ID_Cats int references Cats(ID_Cats) NULL,
    ID_Dogs int references Dogs(ID_Dogs) NULL
)

create table OwnersToPets (
    ID_Owners int references Owners(ID_Owners) NOT NULL,
    ID_Pets int references Pets(ID_Pets) NOT NULL
)
Run Code Online (Sandbox Code Playgroud)

当我通过从数据库导入来创建我的实体对象时,我得到以下信息:

实体关系图

问题是宠物和猫/狗之间的关系。每只猫/狗都有一个宠物参考。每个 Pet 对象都应该有一个 Cat 引用和一个 Dog 引用,其中之一将始终为 null。至少从概念上讲,猫/狗和宠物之间存在 1 比 0..1。

但是,实体数据模型在 Cats / Dogs 和 Pets 之间创建了 0..1 对多关系。

有什么建议?很显然,我的数据库已经存在,并且目前正在与我写的2版本,应用程序的版本1使用所以我宁愿不要有更改数据库太多。

gbn*_*gbn 6

您的问题中缺少很多信息

  • 一只宠物可以有多个主人吗?例如,我们有一只宠物,而不是有一只宠物
  • 您是否期待更多的宠物类型(例如兔子、鸟、鱼)?

您当前模型的问题(如图所示)是 Pets 表:它目前没有增加任何值。当然,您的简化设计现在隐藏了可能有用的信息......

不过,我会提出一些带有假设的想法。

注意:这是正确的设计,EF 是否可以处理它。也就是说,您设计模型,实现数据库,然后让您的客户使用它

选项1:

假设一个所有者可以拥有 0..n 只狗和/或 0..n 只宠物,那么您需要 3 个对象表和 2 个链接表。你不需要宠物。

  • 主人,猫,狗
  • CatOwner 和 DogOwner 的链接表

这允许每只狗和猫有多个主人。你当然可以在 Dog 和 Cat 中只拥有一个 OwnerID 来限制一个所有者。

您可以在此处为 Cat 和 Dog 设置不同的属性。

随着您添加更多类型,这变得笨拙(无论您如何建模所有者)

选项 2:

您是否需要将 Cat 或 Dog 区分为单独的实体?这不就是Pet的一个type属性吗?

**Pet
PetID, PK,
PetType, FK,
PetName
OwnerID, FK (optional, see below)
...
Run Code Online (Sandbox Code Playgroud)

您可以将链接表 PetOwner(多个所有者)或 OwnerID 作为 Pet(单个所有者)中的 FK。

这要求每个 Cat 或 Dog(或 Rabbit、Snake 等)对象具有相同的属性。

选项 3:

最后一种方法是超键/子类型。

这很有用,因为如果结合了选项 1(每只猫和狗的不同属性)和选项 2(保留宠物的想法)的元素

这定义了一个 Pet 对象,该对象恰好具有 Dog、Cat、Rabbit、Snake 等一种子类型。 super 键来自 Pet 中额外的 UQ1,用于强制执行子类型。作为表的宠物也存储了公共属性。

**Pet
PetID, PK, UQ1
PetType, UQ1, 'Dog' or 'Cat'
PetName
OwnerID, FK (optional, see below)
...

**Dog
PetID, PK, FK1
PetType, PK, FK1, constraint = 'Dog'
Breed  -- per Dog,not per Pet
CatsChased
...

**Cat
PetID, PK, FK1
PetType, PK, FK1, constraint = 'Cat'
Breed -- per Cat, not per Pet
MiceEaten
...
Run Code Online (Sandbox Code Playgroud)

注意:UQ1 是 Cat 和 Dog 中 FK 使用的唯一约束。它是 Pet 上的“超级键”,允许通过 FK 设置子类型

同样,您可以将链接表 PetOwner(多个所有者)或 OwnerID 作为 Pet(单个所有者)中的 FK。

注意,PetType 可以是 Dog 和 Cat 中的常量派生列

编辑:添加了宠物、猫和狗的表格。
我认为所有者很明显......

CREATE TABLE PetSpecies (
    PetSpeciesID tinyint NOT NULL IDENTITY (1,1),
    Species varchar(100) NOT NULL,

    CONSTRAINT PK_PetSpecies PRIMARY KEY CLUSTERED (PetSpeciesID),
    CONSTRAINT UQ_PetSpecies UNIQUE (Species)
);
INSERT PetSpecies (Species) VALUES ('Dog');
INSERT PetSpecies (Species) VALUES ('Cat');

CREATE TABLE Pet (
    PetID int NOT NULL IDENTITY (1,1),
    PetSpeciesID tinyint NOT NULL,
    PetName varchar(100) NOT NULL
    OwnerID int NOT NULL,
    ...

    CONSTRAINT PK_Pet PRIMARY KEY CLUSTERED (PetID),
    CONSTRAINT FK_Pet_PetSpecies FOREIGN KEY (PetSpeciesID) 
                                 REFERENCES PetSpecies (PetSpeciesID),
    CONSTRAINT FK_Pet_Owner FOREIGN KEY (OwnerID) REFERENCE Owner (OwnerID),
    CONSTRAINT UQ_SuperKey UNIQUE (PetID, PetSpeciesID)
    );

CREATE TABLE Dog (
    PetID int NOT NULL,
    PetSpeciesID tinyint NOT NULL,
    CatsChased int NOT NULL,

    CONSTRAINT PK_Dog PRIMARY KEY CLUSTERED (PetID, PetSpeciesID),
    -- CONSTRAINT UQ_Dog UNIQUE (PetID),
    CONSTRAINT CK_Cat CHECK (PetSpeciesID = 1 /*dog*/),
    CONSTRAINT FK_Dog_Pet FOREIGN KEY (PetID, PetSpeciesID) 
                          REFERENCES Pet(PetID, PetSpeciesID)
    );

CREATE TABLE Cat (
    PetID int NOT NULL,
    PetSpeciesID tinyint NOT NULL,
    MiceEaten int NOT NULL,

    CONSTRAINT PK_Cat PRIMARY KEY CLUSTERED (PetID, PetSpeciesID),
    -- CONSTRAINT UQ_Cat UNIQUE (PetID),
    CONSTRAINT CK_Cat CHECK (PetSpeciesID = 2 /*cat*/),
    CONSTRAINT FK_Cat_Pet FOREIGN KEY (PetID, PetSpeciesID) 
                          REFERENCES Pet(PetID, PetSpeciesID)
    );
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 猫和狗的PK可以是PetIDPetID, PetSpeciesID。后者更好地匹配外键。

  • 对 cat 和 dog 的唯一约束是可选的,实际上,因为索引包含在 PK 中,并且 PetID 无论如何都是唯一的,如果 PetSpeciesID 被约束为单个值。


小智 0

首先,为什么要将 Pets 表限制为猫或狗。业主也可以两者兼得。而且EF定义的关系是绝对正确的。您想要的是与关系无关的数据操作限制。我不认为你能做到这一点

您面临的要求应该在模型级别解决。您可以使用验证来达到此目的。限制用户输入两者的值。