Alw*_*uff 9 database-design subtypes
下面的例子说明了我面临的问题:
动物有一个种族,可以是猫或狗。猫可以是Siamese或Persian。狗可以是德国牧羊犬或拉布拉多猎犬。
Animal 是一个强大的实体,而其种族是一个属性,可以具有两个提供的值之一( cat 或 dog )。 这两个值都很复杂(我在这里只添加了狗/猫的类型来说明问题,但也可以有猫/狗的名字和一堆其他东西)。
我不知道如何为此示例创建关系表。
我曾尝试使用陈的符号绘制 ER 图,这代表了问题,但作为初学者,我不知道我是否做得对。这是我得到的:
如果我画错了,我深表歉意,如果是这样,请纠正我。我不希望简单地获得“免费解决方案”,而且还希望学习如何处理这个问题,以便我将来可以自己解决。
我唯一想到的是创建两张单独的桌子,一张给猫,一张给狗。此外,Animal表中的race属性将只存储cat或dog值。像这样的东西:
Animal< # Animal_ID, race, other attributes >
Cat < # Cat_ID, $ Animal_ID, breed >
Dog < # Dog_ID, $ Animal_ID, breed >
Run Code Online (Sandbox Code Playgroud)
我真的对我的解决方案有一种不好的感觉,我担心它是错误的,因此下面的问题。
如果需要更多信息,请发表评论,我会尽快更新我的帖子。也可以随意添加适当的标签,因为我在这里很新。
谢谢你。
Sol*_*zky 11
这种情况的正确结构是子类/继承模型,几乎与我在这个答案中提出的概念相同:值的异构有序列表。
这个问题中提出的模型实际上非常接近,因为Animal
实体包含类型(即race
)和所有类型通用的属性。但是,需要进行两个小的更改:
从各自的实体中删除 Cat_ID 和 Dog_ID 字段:
这里的关键概念是一切都是Animal
,而不管race
: Cat
、Dog
、Elephant
等等。鉴于该起点,任何特定race
的Animal
并不真正需要单独的标识符,因为:
Animal_ID
是独一无二的Cat
,Dog
和任何其他race
在未来加入的实体不这样做,由自己,完全代表任何特定的Animal
; 它们仅在与父实体中包含的信息结合使用时才有意义Animal
。因此,在Animal_ID
产权的Cat
,Dog
等实体既是PK和FK回Animal
实体。
区分以下类型breed
:
仅仅因为两个属性共享相同的名称并不一定意味着这些属性是相同的,即使名称相同意味着存在这种关系。在这种情况下,您真正拥有的是实际上CatBreed
和DogBreed
作为单独的“类型”
VARCHAR
但如果您需要存储标准 ASCII 集之外的任何内容,您应该真正使用NVARCHAR
.Race
、CatBreed
、 和DogBreed
)的 ID 字段不是自动递增的(即 T-SQL 中的 IDENTITY),因为它们是应用程序常量(即它们是应用程序的一部分),是应用程序中的静态查找值。数据库并enum
在 C#(或其他语言)中表示为s。如果添加值,它们会在受控情况下添加。对于通过应用程序输入的用户数据,我保留使用自动增量字段。
第一组表是查找/类型表:
CREATE TABLE Race
(
RaceID INT NOT NULL PRIMARY KEY
RaceName VARCHAR(50) NOT NULL
);
CREATE TABLE CatBreed
(
CatBreedID INT NOT NULL PRIMARY KEY,
BreedName VARCHAR(50),
CatBreedAttribute1 INT,
CatBreedAttribute2 VARCHAR(10)
-- other "CatBreed"-specific properties as needed
);
CREATE TABLE DogBreed
(
DogBreedID INT NOT NULL PRIMARY KEY,
BreedName VARCHAR(50),
DogBreedAttribute1 TINYINT
-- other "DogBreed"-specific properties as needed
);
Run Code Online (Sandbox Code Playgroud)
第二个列表是主要的“动物”实体:
CREATE TABLE Animal
(
AnimalID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
RaceID INT NOT NULL, -- FK to Race
Name VARCHAR(50)
-- other "Animal" properties that are shared across "Race" types
);
ALTER TABLE Animal
ADD CONSTRAINT [FK_Animal_Race]
FOREIGN KEY (RaceID)
REFERENCES Race (RaceID);
Run Code Online (Sandbox Code Playgroud)
这第三组表是免费的子类实体完成每个定义Race
的Animal
:
CREATE TABLE AnimalCat
(
AnimalID INT NOT NULL PRIMARY KEY, -- FK to Animal
CatBreedID INT NOT NULL, -- FK to CatBreed
HairColor VARCHAR(50) NOT NULL
-- other "Cat"-specific properties as needed
);
ALTER TABLE AnimalCat
ADD CONSTRAINT [FK_AnimalCat_CatBreed]
FOREIGN KEY (CatBreedID)
REFERENCES CatBreed (CatBreedID);
ALTER TABLE AnimalCat
ADD CONSTRAINT [FK_AnimalCat_Animal]
FOREIGN KEY (AnimalID)
REFERENCES Animal (AnimalID);
CREATE TABLE AnimalDog
(
AnimalID INT NOT NULL PRIMARY KEY, -- FK to Animal
DogBreedID INT NOT NULL, -- FK to DogBreed
HairColor VARCHAR(50) NOT NULL
-- other "Dog"-specific properties as needed
);
ALTER TABLE AnimalDog
ADD CONSTRAINT [FK_AnimalDog_DogBreed]
FOREIGN KEY (DogBreedID)
REFERENCES DogBreed (DogBreedID);
ALTER TABLE AnimalDog
ADD CONSTRAINT [FK_AnimalDog_Animal]
FOREIGN KEY (AnimalID)
REFERENCES Animal (AnimalID);
Run Code Online (Sandbox Code Playgroud)
使用共享breed
类型的模型显示在“附加说明”部分之后。
breed
似乎是混淆的焦点。jcolebrand(在对该问题的评论中)建议这breed
是一个在不同race
s之间共享的属性,而其他两个答案将其集成到他们的模型中。然而,这是一个错误,因为 的值breed
不会在 的不同值之间共享race
。是的,我知道其他两个提出的模型试图通过使解决这一问题race
的父母breed
。虽然这在技术上解决了关系问题,但它无助于解决如何处理非公共属性的整体建模问题,也无助于解决race
没有abreed
. 但是,如果保证这样的财产存在于所有Animal
s,我也会包括一个选项(如下)。Animal
),或race
s 的所有属性都存储在Animal
实体中,这是表示此数据的一种非常扁平(且几乎非关系)的方式。是的,人们一直都这样做,但这意味着每行有许多 NULL 字段用于不适合该特定的属性,并且race
知道每行哪些字段与该race
记录的特定相关联。race
of 。并且即使 ALL s 有 a ,由于之前已经提到的内容,这也不会改变结构:这取决于 the (即for与for 不同)。Animal
breed
Animal
breed
breed
breed
race
breed
Cat
breed
Dog
请注意:
下面的 SQL 可以在与上面介绍的模型相同的数据库中运行:
Race
表是相同的Breed
表是新Animal
表都附加了一个2
Breed
现在是一个共同的财产,Race
在主要/父实体中没有注意到似乎是不对的(即使它在技术上是正确的)。所以,RaceID
和BreedID
都用 表示Animal2
。为了防止之间的不匹配RaceID
所提到的Animal2
和BreedID
这是一个不同的RaceID
,我加了FK两个RaceID, BreedID
引用在这些领域的唯一约束Breed
表。我通常不屑将 FK 指向 UNIQUE CONSTRAINT,但这是这样做的少数正当理由之一。UNIQUE CONSTRAINT 在逻辑上是一个“备用密钥”,这使得它对这种用途有效。另请注意,该Breed
表仍然有一个 PK BreedID
。
BreedID
跨越不同的值重复RaceID
。BreedID
,因此仍然可以在Breed
没有RaceID
可用的情况下引用 的特定值。Breed
(这也是我更喜欢Race
-specificBreed
表的原因)。
Breed
具有相同的属性。在这个模型中没有简单的方法在Dog
“品种”和Elephant
“品种”之间具有不同的属性。但是,仍然有一种方法可以做到这一点,在“最终编辑”部分中进行了说明。Breed
以上的种族中共享一个。我不确定这是否是可取的(或者可能不是在动物的概念中,但可能在使用这种类型模型的其他情况下),但在这里是不可能的。CREATE TABLE Race
(
RaceID INT NOT NULL PRIMARY KEY,
RaceName VARCHAR(50) NOT NULL
);
CREATE TABLE Breed
(
BreedID INT NOT NULL PRIMARY KEY,
RaceID INT NOT NULL, -- FK to Race
BreedName VARCHAR(50)
);
ALTER TABLE Breed
ADD CONSTRAINT [UQ_Breed]
UNIQUE (RaceID, BreedID);
ALTER TABLE Breed
ADD CONSTRAINT [FK_Breed_Race]
FOREIGN KEY (RaceID)
REFERENCES Race (RaceID);
CREATE TABLE Animal2
(
AnimalID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
RaceID INT NOT NULL, -- FK to Race, FK to Breed
BreedID INT NOT NULL, -- FK to Breed
Name VARCHAR(50)
-- other properties common to all "Animal" types
);
ALTER TABLE Animal2
ADD CONSTRAINT [FK_Animal2_Race]
FOREIGN KEY (RaceID)
REFERENCES Race (RaceID);
-- This FK points to the UNIQUE CONSTRAINT on Breed, _not_ to the PK!
ALTER TABLE Animal2
ADD CONSTRAINT [FK_Animal2_Breed]
FOREIGN KEY (RaceID, BreedID)
REFERENCES Breed (RaceID, BreedID);
CREATE TABLE AnimalCat2
(
AnimalID INT NOT NULL PRIMARY KEY, -- FK to Animal
HairColor VARCHAR(50) NOT NULL
);
ALTER TABLE AnimalCat2
ADD CONSTRAINT [FK_AnimalCat2_Animal2]
FOREIGN KEY (AnimalID)
REFERENCES Animal2 (AnimalID);
CREATE TABLE AnimalDog2
(
AnimalID INT NOT NULL PRIMARY KEY,
HairColor VARCHAR(50) NOT NULL
);
ALTER TABLE AnimalDog2
ADD CONSTRAINT [FK_AnimalDog2_Animal2]
FOREIGN KEY (AnimalID)
REFERENCES Animal2 (AnimalID);
Run Code Online (Sandbox Code Playgroud)
Breed
,它是能够使用相同的子类/继承的概念,但与Breed
作为主要实体。在此设置中,Breed
表将具有所有类型的通用属性Breed
(就像Animal
表一样),RaceID
并表示类型Breed
(与Animal
表中相同)。然后,您将拥有子类表,例如BreedCat
、BreedDog
等。对于较小的项目,这可能被认为是“过度设计”,但它被认为是可以从中受益的情况的一种选择。对于这两种方法,有时将视图创建为完整实体的快捷方式会有所帮助。例如,考虑:
CREATE VIEW Cats AS
SELECT an.AnimalID,
an.RaceID,
an.Name,
-- other "Animal" properties that are shared across "Race" types
cat.CatBreedID,
cat.HairColor
-- other "Cat"-specific properties as needed
FROM Animal an
INNER JOIN AnimalCat cat
ON cat.AnimalID = an.AnimalID
-- maybe add in JOIN(s) and field(s) for "Race" and/or "Breed"
Run Code Online (Sandbox Code Playgroud)CreatedDate
字段将被添加到Animal
表中。在任何子类表(例如AnimalCat
)中都不需要此字段,因为应在事务中同时为两个表插入行。LastModifiedDate
将向Animal
表和所有子类表中添加一个字段。仅当更新该特定表时,此字段才会更新:如果更新发生在AnimalCat
但不在Animal
特定的 中AnimalID
,则只会设置LastModifiedDate
字段 in AnimalCat
。 归档时间: |
|
查看次数: |
1679 次 |
最近记录: |