不加字段的优雅归一化,额外table。最好的关系

Elegant normalization without adding fields, extra table. Best relationship

我有 2 个 table 正在尝试正常化。问题是我不想创建一个带有新字段的副手 table,尽管 link table 可能有效。传达 "Nintendo" 条目既是发布者又是开发者的最优雅方式是什么?我不希望 "Nintendo" 被重复。我认为 many-to-many 关系可能是这里的关键。

我想强调的是,我绝对希望开发商和发行商 table 留下来。我不介意在具有新关系的 2 之间建立 link。

这是我要标准化的 2 个 table:

以下是我尝试过的解决方案(我不喜欢):

我想你想要这样的东西:

Game_Company
ID    Name
 1    Retro Studios
 2    HAL Laboratories
 3    Nintendo
 ...

Company_Role
ID    Name
 1    Developer
 2    Publisher
 ...

Game_Company_Role
CompanyID    RoleID
        1         1
        2         1
        3         1
        3         2
 ...

获取具有角色 'Developer' 的所有公司的列表:

SELECT gc.name
FROM Game_Company gc JOIN Game_Company_Role gcr ON gcr.CompanyID=gc.ID
WHERE gcr.RoleID = 1

这是解决问题的一种通用方法,您可能会感兴趣。正如@Dour High Arch 在他的解决方案中指出的那样,开发者和发布者只是 'party' 的角色。每个部分都有 0,1 或更多角色与给定的产品和角色可能 overlap.This 是好的和坏的。例如,一个产品可能由 5 个开发人员开发,但最多由 1 个发布者发布。 我选择引入 serial_id 作为系统生成的 PK,但这不是强制性的。您可以将 3FK 用作 PK 而不是使用 serial_id.

请注意,将一方作为不同实体类型的概括并不总是好的,因为 1 个或多个列必须设置为非强制性的,如果它不是所有各方都通用的,但是,这在实际应用。

约定:

name_PK = 主键,

name_FK = 外键

你的两个table没问题。

其实你只需要

developer(name) -- company [name] is a developer
publisher(name) -- company [name] is a publisher

您的更改与规范化无关。规范化从不创建新的列名。 “我不希望“任天堂”被复制”是错误的想法。值出现在多个地方本身并没有错。查看 sqlvogel 和我自己的答案 here

但是:根据行在您的 table 之一中的含义,可能有更好的设计来减少错误,因为两个 table 的值可能是“约束”即相互依赖。 “冗余”有关,但它与约束有关,不涉及规范化。为了让我们解决这个问题,您必须根据世界情况准确地告诉我们一行何时进入每个 table。

如果你不想重复 strings 出于实现(依赖)的原因(space 采取或以更多连接为代价的操作速度)然后添加名称 ID 和字符串(实际上是公司 ID 和名称)的 table,并用公司 ID 列和值替换旧名称列和值。但这不是规范化,而是为了 implementation-dependent 数据优化权衡而使您的架构复杂化。 (你应该证明这是需要的并且有效。)

目前接受的answer(tables Game_Company,Company_Role & Game_Company_Role)只是增加了很多冗余数据。就像你的问题加了三个多余的table。原来的两个table已经说了哪些公司是开发商,哪些公司是发行商。其他 table 只是 views/queries 两个!

如果您想要一个新的 table 用于“[id] 标识一个名为 [name] 的公司...”,那么这是开发者和发布者作为超类型公司的子类型的情况。搜索数据库子类型。参见 this answer。然后您将使用公司 ID 而不是名称来标识公司。然后,您还可以通过使用公司 ID 作为 table 的开发人员和发布者以及其他任何地方的唯一列来进一步简化 (!),而不是 developer_id 和 publisher_id。

“冗余”不是指值出现在多个地方。它是关于多行说明应用程序的相同内容。当使用这样的设计时,有两个基本问题:说某些事情涉及多行(而规范化版本只涉及一行);并且没有办法一次只说一件事(规范化可以提供帮助)。如果你对任天堂做出了两个不同的独立陈述,那么你需要两个 table 并且每个陈述中都提到了任天堂。有关应用程序的重新行声明请参阅 this. (And search my other answers re a table's "statement" or criterion".) Normalization helps because it replaces tables whose rows state things of the form "... AND ..." by other tables that state the "..." separately. See this and this。 (规范化通常被错误地认为涉及或包括避免多个相似的列,避免值具有重复结构的列 and/or 用 id 替换字符串,但尽管这些可能是好的设计思想,但它们不是规范化。)


在评论、聊天和另一个回答中你给出了这个出发点:

这是最简单的设计。 (我假设游戏名称不是唯一的,因此您需要 game_ids。)

-- game [game_id] with title [title] released on [release_date] is rated [rating]
game(game_id,title,release_date,rating)
game_developer(game_id,name) -- game [game_id] is developed by company [name]
game_publisher(game_id,name) -- game [game_id] is published by company [name]
game_platform(game_id,name) -- game [game_id] is on platform [name]

只有当你想要一个单独的公司列表,这样一个公司可以存在而不开发或发布时 and/or 可以有自己的数据你需要添加:

company(name,...) -- [name] identifies a company

只有当您想要 role-specific 开发者和发布者的数据时,您才需要添加:

developer(name,...) -- developer [name] has ...
publisher(name,...) -- publisher [name] has ...

各个选项的相关外键很简单

None 您的版本 需要 _id。您的版本 2 和 3 将无法使用,因为它们没有说明哪些公司开发游戏或哪些公司发布游戏。你不需要需要角色,但如果你有角色(Verison 2)那么你需要一个table“游戏[game_id]有公司[name]作为[角色]”。否则(版本 3)你需要 tables 来表示“[game_id] 由公司 [name] 开发”和“游戏 [game_id] 由公司 [name] 发布”。无论您与我的设计有何不同,请问问自己 为什么 您有额外的结构,为什么没有它也可以,以及(可能)为什么您会明确想要它。

这里是评论提出的三个最终解决方案。你可以看到 table 从顶部被分解 "un-normalized" table.

规则如下:

  • 1 个游戏可以有 1 个或多个开发者,1 个开发者可以有 1 个或多个游戏。
  • 1 个游戏可以有 1 个或多个发行商,1 个发行商可以有 1 个或多个游戏。
  • 1个游戏可以有1个或多个平台,1个平台可以有1个或多个游戏。

版本 1

我将 2 个 "Nintendo" 条目留为红色。根据研究和实施,这在技术上不是冗余数据。在 philipxy 的回答下查看我的评论。这看起来简单而优雅。 4 table 具有 many-to-many 关系。

这里是关系图(4table和3linktable):

版本 2

版本 1 "repeats" "Nintendo" 但版本 2 有一个 "Company" table。比较两个不同的版本。什么是正确的方法?

版本 3

这是 philipxy 所说的子类型。这个版本怎么样?