不知道如何将可变实体转化为关系表

Alw*_*uff 9 database-design subtypes

介绍和相关信息:

下面的例子说明了我面临的问题:

动物有一个种族,可以是可以是SiamesePersian可以是德国牧羊犬拉布拉多猎犬

Animal 是一个强大的实体,而其种族是一个属性,可以具有两个提供的值之一( cat 或 dog )。 这两个值都很复杂(我在这里只添加了狗/猫的类型来说明问题,但也可以有猫/狗的名字和一堆其他东西)。

问题:

我不知道如何为此示例创建关系表。

我解决问题的努力:

我曾尝试使用陈的符号绘制 ER 图,这代表了问题,但作为初学者,我不知道我是否做得对。这是我得到的:

在此处输入图片说明

如果我画错了,我深表歉意,如果是这样,请纠正我。我不希望简单地获得“免费解决方案”,而且还希望学习如何处理这个问题,以便我将来可以自己解决。

我唯一想到的是创建两张单独的桌子,一张给猫,一张给狗。此外,Animal表中的race属性将只存储catdog值。像这样的东西:

Animal< # Animal_ID, race, other attributes >
Cat < # Cat_ID, $ Animal_ID, breed >
Dog < # Dog_ID, $ Animal_ID, breed >
Run Code Online (Sandbox Code Playgroud)

我真的对我的解决方案有一种不好的感觉,我担心它是错误的,因此下面的问题。

问题:

  • 如何将我的示例转换为 ER 图?
  • 如何将该 ER 图转换为关系表?

如果需要更多信息,请发表评论,我会尽快更新我的帖子。也可以随意添加适当的标签,因为我在这里很新。

谢谢你。

Sol*_*zky 11

这种情况的正确结构是子类/继承模型,几乎与我在这个答案中提出的概念相同:值的异构有序列表

这个问题中提出的模型实际上非常接近,因为Animal实体包含类型(即race)和所有类型通用的属性。但是,需要进行两个小的更改:

  1. 从各自的实体中删除 Cat_ID 和 Dog_ID 字段:

    这里的关键概念是一切都是Animal,而不管race: CatDogElephant等等。鉴于该起点,任何特定raceAnimal并不真正需要单独的标识符,因为:

    1. Animal_ID是独一无二的
    2. CatDog和任何其他race在未来加入的实体不这样做,由自己,完全代表任何特定的Animal; 它们仅在与父实体中包含的信息结合使用时才有意义Animal

    因此,在Animal_ID产权的CatDog等实体既是PK和FK回Animal实体。

  2. 区分以下类型breed

    仅仅因为两个属性共享相同的名称并不一定意味着这些属性是相同的,即使名称相同意味着存在这种关系。在这种情况下,您真正​​拥有的是实际上CatBreedDogBreed作为单独的“类型”

初始笔记

  1. SQL 特定于 Microsoft SQL Server(即 T-SQL)。意思是,要小心数据类型,因为它们在所有 RDBMS 中并不相同。例如,我正在使用,VARCHAR但如果您需要存储标准 ASCII 集之外的任何内容,您应该真正使用NVARCHAR.
  2. “类型”表(RaceCatBreed、 和DogBreed)的 ID 字段不是自动递增的(即 T-SQL 中的 IDENTITY),因为它们是应用程序常量(即它们是应用程序的一部分),是应用程序中的静态查找值。数据库并enum在 C#(或其他语言)中表示为s。如果添加值,它们会在受控情况下添加。对于通过应用程序输入的用户数据,我保留使用自动增量字段。
  3. 我使用的命名约定是命名每个子类表,从主类名开始,后跟子类名。这有助于组织表并清楚地指示(无需查看 FK)子类表与主实体表的关系。
  4. 请参阅最后的“最终编辑”部分以获取有关视图的注释。

“品种”即“种族”——特定方法

品种作为种族特定的图表
第一组表是查找/类型表:

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)

这第三组表是免费的子类实体完成每个定义RaceAnimal

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类型的模型显示在“附加说明”部分之后。

补充说明

  1. 的概念breed似乎是混淆的焦点。jcolebrand(在对该问题的评论中)建议这breed是一个在不同races之间共享的属性,而其他两个答案将其集成到他们的模型中。然而,这是一个错误,因为 的值breed不会在 的不同值之间共享race。是的,我知道其他两个提出的模型试图通过使解决这一问题race的父母breed。虽然这在技术上解决了关系问题,但它无助于解决如何处理非公共属性的整体建模问题,也无助于解决race没有abreed. 但是,如果保证这样的财产存在于所有Animals,我也会包括一个选项(如下)。
  2. vijayp 和 DavidN(似乎是相同的)提出的模型不起作用,因为:
    1. 他们要么
      1. 不允许存储非公共属性(至少不允许存储 any 的单个实例Animal),或
      2. 要求所有races 的所有属性都存储在Animal实体中,这是表示此数据的一种非常扁平(且几乎非关系)的方式。是的,人们一直都这样做,但这意味着每行有许多 NULL 字段用于不适合该特定的属性,并且race知道每行哪些字段与该race记录的特定相关联。
    2. 它们不允许将来添加没有作为属性的raceof 。并且即使 ALL s 有 a ,由于之前已经提到的内容,这也不会改变结构:这取决于 the (即for与for 不同)。AnimalbreedAnimalbreedbreedbreedracebreedCatbreedDog

“品种”作为共同/共享财产方法

在此处输入图片说明
请注意:

  1. 下面的 SQL 可以在与上面介绍的模型相同的数据库中运行:

    1. Race表是相同的
    2. Breed表是新
    3. 这三个Animal表都附加了一个2
  2. 即使Breed现在是一个共同的财产,Race在主要/父实体中没有注意到似乎是不对的(即使它在技术上是正确的)。所以,RaceIDBreedID都用 表示Animal2。为了防止之间的不匹配RaceID所提到的Animal2BreedID这是一个不同的RaceID,我加了FK两个RaceID, BreedID引用在这些领域的唯一约束Breed表。我通常不屑将 FK 指向 UNIQUE CONSTRAINT,但这是这样做的少数正当理由之一。UNIQUE CONSTRAINT 在逻辑上是一个“备用密钥”,这使得它对这种用途有效。另请注意,该Breed表仍然有一个 PK BreedID
    1. 原因只有在合并域,没有唯一约束的一种PK不会是它将允许同BreedID跨越不同的值重复RaceID
    2. 不切换 PK 和 UNIQUE CONSTRAINT 的原因是,这可能不是 的唯一用法BreedID,因此仍然可以在Breed没有RaceID可用的情况下引用 的特定值。
  3. 虽然以下模型确实有效,但它在共享概念方面存在两个潜在缺陷Breed(这也是我更喜欢Race-specificBreed表的原因)。
    1. 有一个隐含的假设,即 的所有值都Breed具有相同的属性。在这个模型中没有简单的方法在Dog“品种”和Elephant“品种”之间具有不同的属性。但是,仍然有一种方法可以做到这一点,在“最终编辑”部分中进行了说明。
    2. 没有办法在一个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)


最终编辑(希望 ;-)

  1. 关于处理类型之间不同的性能的可能性(然后难度)Breed,它能够使用相同的子类/继承的概念,但与Breed作为主要实体。在此设置中,Breed表将具有所有类型的通用属性Breed(就像Animal表一样),RaceID并表示类型Breed(与Animal表中相同)。然后,您将拥有子类表,例如BreedCatBreedDog等。对于较小的项目,这可能被认为是“过度设计”,但它被认为是可以从中受益的情况的一种选择。
  2. 对于这两种方法,有时将视图创建为完整实体的快捷方式会有所帮助。例如,考虑:

    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)
  3. 虽然不是逻辑实体的一部分,但在表中具有审计字段以至少了解记录何时被插入和更新是相当普遍的。所以在实际中:
    1. 一个CreatedDate字段将被添加到Animal表中。在任何子类表(例如AnimalCat)中都不需要此字段,因为应在事务中同时为两个表插入行。
    2. LastModifiedDate将向Animal表和所有子类表中添加一个字段。仅当更新该特定表时,此字段才会更新:如果更新发生在AnimalCat但不在Animal特定的 中AnimalID,则只会设置LastModifiedDate字段 in AnimalCat

  • 不知怎的,我感觉你完全明白我的问题是什么。我会给你链接的答案看看,并仔细研究。对表进行简单的定义也很好(如果 SQL 查询太多而您目前无法编写)。如果您决定使用 SQL 查询或表定义更新您的帖子,请给我留言。再次感谢你。此致。 (2认同)