为具有多个多对多关系的视频游戏业务领域设计数据库

dpo*_*tar 16 normalization database-design denormalization many-to-many

我对数据库设计比较陌生,我决定制作自己的假设数据库以进行实践。但是,我无法对其进行建模和规范化,因为我认为存在许多多对多 (M:N) 关系。

一般场景描述

该数据库旨在保留有关在塞尔达系列中工作过的各种人物的数据。我想跟踪的控制台(S) ,一个游戏可以玩上,员工是曾在部分游戏的发展,乔布斯员工有(很多员工在不同的工作职位在多个游戏等)

商业规则

  • 多个员工可以在多个游戏上工作。
  • 多个游戏可以在同一个控制台上
  • 多个控制台可以是同一个游戏的平台。
  • 多个员工可以拥有相同的Job
  • 一个Employee可以有多个Jobs
  • 一个游戏可以有多个员工
  • 一个游戏在它的开发过程中可以有多种类型的工作
  • 多个游戏可以附加相同类型的工作
  • 一个控制台可以有多个人在处理它。
  • 一个可以在多个控制台上工作。

属性名称和样本值

  • Employee Name,可以分为FirstLast(例如“John”和“Doe”)
  • 游戏名称(例如“时之笛”)
  • 职位(例如“关卡设计”、“导演”、“沉着”、“关卡设计师”、“程序员”、“本地化”等)。
  • 控制台名称(例如“Game Boy Advance”)

问题

到目前为止,无论我设计什么,似乎到处都存在感兴趣的实体类型之间的数据冗余和 M:N 关系。不过我觉得数据库设计者一定会经常遇到这样的问题,所以一定要有解决的办法。


注意:我很容易找到填充表格的数据,问题是将它组织到一个包含规范化表格的数据库中。

MDC*_*CCL 18

是的,多对多(简称 M:N)关联或关系的识别是数据库从业者在布置概念模式时非常常见的情况。所述基数比率的关联出现在性质非常不同的业务环境中,并且通过例如 SQL-DDL 安排在逻辑级别正确表示时,它们不会引入有害的冗余。

这样,数据库建模练习的目标应该是高精度地反映感兴趣的业务上下文的相关特征;因此,如果您正确地识别出有许多 M:N 关联,那么您必须在 (a) 概念模式和 (b) 相应的逻辑级声明中表达它们,无论它们有多少连接 - 或任何必须解决其他类型的基数比率。

商业规则

您提供了一个充分背景化的问题,并澄清了您正在使用的数据库纯粹是假设性的,这一点很重要,因为我认为像所考虑的那样的“真实世界”业务场景会更广泛因此,将意味着更复杂的信息需求。

我决定 (1) 对您提供的业务规则进行一些修改和扩展,以便 (2) 生成更具描述性的概念模式——尽管仍然是假设性的——。以下是我整理的一些公式:

  • 一个1可以是一个或一个组织
  • 一个Party被完全分类为一个PartyType
  • 一个PartyType进行分类零一或一对多的缔约方
  • 一个组织开发零一或多产品
  • 一个产品或者是一个系统或一个游戏
  • 一个产品按完全一个ProductType分类
  • 一个系统由恰好一个SystemType编目
  • 一个游戏可以通过一个一对多播放系统
  • 一个系统用于玩一对多游戏
  • 一个游戏是由零一或一对多分类流派
  • 一种类型对零一或多游戏进行分类
  • 一个产品产生一对多的工作
  • 一个工作是通过零一或一对多履行人民,谁在玩角色合作者
  • 一个是零一或多工作合作者

1 Party是在法律上下文中使用的一个术语,用于指代组成单个实体的个人或一组个人,因此该名称适合代表人员组织


IDEF1X图

随后,我创建了图 1 中所示的 IDEF1X 2图表(确保单击链接以更高的分辨率查看它),将上面显示的业务规则(以及其他一些看起来相关的规则)整合到单个图形设备中:

图 1 - Video Gae Jobs IDEF1X 图


2 对于信息建模集成定义IDEF1X)是被确立为一个非常可取的数据建模技术标准是由美国在1993年12月美国国家标准与技术研究院(NIST)。它基于 (a) 关系模型的唯一创始人,即 EF Codd 博士撰写的早期理论材料;关于 (b) 数据的实体关系视图,由PP Chen 博士开发;以及 (c) 逻辑数据库设计技术,由 Robert G. Brown 创建。


如您所见,我仅通过相应的关联实体类型描述了三个 M:N 关联,即:

  • 合作者
  • 系统游戏
  • 游戏类型

在其他方面,有两个不同的超类型-子类型结构,其中:

  • PersonOrganizationParty 的互斥实体子类型,它们的实体超类型

  • ProductSystemGame的超类型,它们又是互斥的子类型

如果您不熟悉超类型-子类型关联,您可能会找到帮助,例如,我对以下问题的回答:

说明性的逻辑 SQL-DDL 布局

随后,我们必须确保在逻辑层面上:

  • 每个实体类型由一个单独的基表表示
  • 适用实体类型的每个属性都由特定表示
  • 为每一固定了一个确切的数据类型,以确保它包含的所有都属于一个特定且定义明确的集合,无论是 INT、DATETIME、CHAR 等(当然,当使用例如FirebirdPostgreSQL 时,您可能希望使用功能更强大的 DOMAIN)
  • 配置多个约束(声明式)以保证所有表中保留的形式的断言符合在概念级别确定的业务规则

所以我根据之前显示的 IDEF1X 图声明了以下 DDL 安排:

CREATE TABLE PartyType ( -- Stands for an independent entity type.
    PartyTypeCode CHAR(1)  NOT NULL, -- To retain 'P' or 'O'.
    Name          CHAR(30) NOT NULL, -- To keep 'Person' or 'Organization'.
    --  
    CONSTRAINT PartyType_PK PRIMARY KEY (PartyTypeCode)
);

CREATE TABLE Party ( -- Represents an entity supertype.
    PartyId         INT       NOT NULL,
    PartyTypeCode   CHAR(1)   NOT NULL, -- To hold the value that indicates the type of the row denoting the complementary subtype occurrence: either 'P' for 'Person' or 'O' for 'Organization'.
    CreatedDateTime TIMESTAMP NOT NULL,  
    --
    CONSTRAINT Party_PK            PRIMARY KEY (PartyId),
    CONSTRAINT PartyToPartyType_FK FOREIGN KEY (PartyTypeCode)
        REFERENCES PartyType (PartyTypeCode)
);

CREATE TABLE Person ( -- Denotes an entity subtype.
    PersonId        INT      NOT NULL, -- To be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
    FirstName       CHAR(30) NOT NULL,
    LastName        CHAR(30) NOT NULL,
    GenderCode      CHAR(3)  NOT NULL,
    BirthDate       DATE     NOT NULL,
    --
    CONSTRAINT Person_PK PRIMARY KEY        (PersonId),
    CONSTRAINT Person_AK UNIQUE             (FirstName, LastName, GenderCode, BirthDate), -- Composite ALTERNATE KEY.
    CONSTRAINT PersonToParty_FK FOREIGN KEY (PersonId)
        REFERENCES Party (PartyId)
);

CREATE TABLE Organization ( -- Stands for an entity subtype.
    OrganizationId  INT      NOT NULL, -- To be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
    Name            CHAR(30) NOT NULL,
    FoundingDate    DATE     NOT NULL,
    --
    CONSTRAINT Organization_PK        PRIMARY KEY (OrganizationId),
    CONSTRAINT Organization_AK        UNIQUE      (Name), -- Single-column ALTERNATE KEY.
    CONSTRAINT OrganizationToParty_FK FOREIGN KEY (OrganizationId)
        REFERENCES Party (PartyId)
);

CREATE TABLE ProductType ( -- Represents an independent entity type.
    ProductTypeCode CHAR(1)  NOT NULL, -- To enclose the values 'S' and 'G' in the corresponding rows.
    Name            CHAR(30) NOT NULL, -- To comprise the values 'System' and 'Person' in the respective rows.
    --
    CONSTRAINT ProductType_PK PRIMARY KEY (ProductTypeCode)
);

CREATE TABLE Product ( -- Denotes an entity supertype.
    OrganizationId  INT      NOT NULL,
    ProductNumber   INT      NOT NULL,
    ProductTypeCode CHAR(1)  NOT NULL, -- To keep the value that indicates the type of the row denoting the complementary subtype occurrence: either 'S' for 'System' or 'G' for 'Game'.
    CreatedDateTime DATETIME NOT NULL,
    --
    CONSTRAINT Product_PK               PRIMARY KEY (OrganizationId, ProductNumber), -- Composite PRIMARY KEY.
    CONSTRAINT ProductToOrganization_FK FOREIGN KEY (OrganizationId)
        REFERENCES Organization (OrganizationId),
    CONSTRAINT ProductToProductType_FK  FOREIGN KEY (ProductTypeCode)
        REFERENCES ProductType (ProductTypeCode)
);

CREATE TABLE SystemType ( -- Stands for an independent entity type.
    SystemTypeCode CHAR(1)  NOT NULL,
    Name           CHAR(30) NOT NULL,
     --
    CONSTRAINT SystemType_PK PRIMARY KEY (SystemTypeCode)
);

CREATE TABLE MySystem ( -- Represents a dependent entity type.
    OrganizationId   INT      NOT NULL, -- To be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
    SystemNumber     INT      NOT NULL,
    SystemTypeCode   CHAR(1)  NOT NULL,
    ParticularColumn CHAR(30) NOT NULL,
    --
    CONSTRAINT System_PK              PRIMARY KEY (OrganizationId, SystemNumber),
    CONSTRAINT SystemToProduct_FK     FOREIGN KEY (OrganizationId, SystemNumber)
        REFERENCES Product (OrganizationId, ProductNumber),
    CONSTRAINT SystemToSystemType_FK  FOREIGN KEY (SystemTypeCode)
        REFERENCES SystemType (SystemTypeCode)
);

CREATE TABLE Game ( -- Denotes an entity subtype.
    OrganizationId INT      NOT NULL, -- To be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
    GameNumber     INT      NOT NULL,
    SpecificColumn CHAR(30) NOT NULL,
    --
    CONSTRAINT Game_PK          PRIMARY KEY (OrganizationId, GameNumber),
    CONSTRAINT GameToProduct_FK FOREIGN KEY (OrganizationId, GameNumber)
         REFERENCES Product (OrganizationId, ProductNumber)
);

CREATE TABLE Genre ( -- Stands for an independent entity type.
    GenreNumber INT      NOT NULL,
    Name        CHAR(30) NOT NULL,  
    Description CHAR(90) NOT NULL,
    --
    CONSTRAINT Genre_PK  PRIMARY KEY (GenreNumber),
    CONSTRAINT Genre_AK1 UNIQUE      (Name),
    CONSTRAINT Genre_AK2 UNIQUE      (Description)
);

CREATE TABLE SystemGame ( -- Represents an associative entity type or M:N association.
    SystemOrganizationId INT      NOT NULL,  
    SystemNumber         INT      NOT NULL,  
    GameOrganizationId   INT      NOT NULL,    
    GameNumber           INT      NOT NULL,
    CreatedDateTime      DATETIME NOT NULL,
    -- 
    CONSTRAINT SystemGame_PK         PRIMARY KEY (SystemOrganizationId, SystemNumber, GameOrganizationId, GameNumber), -- Composite PRIMARY KEY.
    CONSTRAINT SystemGameToSystem_FK FOREIGN KEY (SystemOrganizationId, SystemNumber) -- Multi-column FOREIGN KEY.
        REFERENCES MySystem (OrganizationId, SystemNumber),
    CONSTRAINT SystemGameToGame_FK   FOREIGN KEY (SystemOrganizationId, GameNumber) -- Multi-column FOREIGN KEY.
        REFERENCES Game (OrganizationId, GameNumber)  
);

CREATE TABLE GameGenre ( -- Denotes an associative entity type or M:N association.
    GameOrganizationId INT      NOT NULL,    
    GameNumber         INT      NOT NULL,
    GenreNumber        INT      NOT NULL,  
    CreatedDateTime    DATETIME NOT NULL,
    -- 
    CONSTRAINT GameGenre_PK        PRIMARY KEY (GameOrganizationId, GameNumber, GenreNumber), -- Composite PRIMARY KEY.
    CONSTRAINT GameGenreToGame_FK  FOREIGN KEY (GameOrganizationId, GameNumber)
        REFERENCES Game (OrganizationId, GameNumber), -- Multi-column FOREIGN KEY.
    CONSTRAINT GameGenreToGenre_FK FOREIGN KEY (GenreNumber)
        REFERENCES Genre (GenreNumber) 
);

CREATE TABLE Job ( -- Stands for an associative entity type or M:N association.
    OrganizationId  INT      NOT NULL,
    ProductNumber   INT      NOT NULL,
    JobNumber       INT      NOT NULL,
    Title           CHAR(30) NOT NULL,  
    CreatedDateTime DATETIME NOT NULL,
    --
    CONSTRAINT Job_PK          PRIMARY KEY (OrganizationId, ProductNumber, JobNumber), -- Composite PRIMARY KEY.
    CONSTRAINT Job_AK          UNIQUE      (Title), -- Single-column ALTERNATE KEY.
    CONSTRAINT JobToProduct_FK FOREIGN KEY (OrganizationId, ProductNumber) -- Multi-column FOREIGN KEY.
        REFERENCES Product (OrganizationId, ProductNumber)
);

CREATE TABLE Collaborator ( -- Represents an associative entity type or M:N association.
    CollaboratorId   INT      NOT NULL,    
    OrganizationId   INT      NOT NULL,
    ProductNumber    INT      NOT NULL,
    JobNumber        INT      NOT NULL,
    AssignedDateTime DATETIME NOT NULL,
    --
    CONSTRAINT Collaborator_PK         PRIMARY KEY (CollaboratorId, OrganizationId, ProductNumber, JobNumber), -- Composite PRIMARY KEY.
    CONSTRAINT CollaboratorToPerson_FK FOREIGN KEY (CollaboratorId)
    REFERENCES Person (PersonId),  
    CONSTRAINT CollaboratorToJob_FK    FOREIGN KEY (OrganizationId, ProductNumber, JobNumber) -- Multi-column FOREIGN KEY.
       REFERENCES Job (OrganizationId, ProductNumber, JobNumber)
);
Run Code Online (Sandbox Code Playgroud)

需要强调的是,在多个表中存在复合PRIMARY KEY 约束的声明,它们代表概念实体类型之间发生的连接层次结构,当例如表达 SELECT 时,这种安排对于数据检索非常有益包含 JOIN 子句以获取派生表的操作。

是的,(i)每个 M:N 关联和(ii)每个关联实体类型都由(iii)逻辑 DDL 结构中的相应表表示,因此要特别注意 PRIMARY 和 FOREIGN KEY 约束(以及我留下的注释)代表这些概念元素的表格,因为它们有助于确保相关行之间的连接满足适用的基数比。

EF Codd 博士从关系范式的起源开始就引入了复合键的使用,正如他在 1970 年题为“大型共享数据库的关系模型”(准确地说,还介绍了处理概念性 M:N 关联的最优雅方法)。

我放置了 一个 db<>fiddle一个 SQL Fiddle,它们都在 Microsoft SQL Server 2014 上运行,以便可以“实际”测试结构。

正常化

归一化是一个逻辑级别的过程,它基本上意味着:

  1. 通过第一范式消除非原子列,以便使用数据子语言(例如 SQL)更容易处理数据操作和压缩。

  2. 借助连续的范式摆脱特定表的列之间的不良依赖关系,以避免更新异常

自然,人们必须考虑所讨论的表和列所承载的含义

我喜欢将规范化视为一种建立在科学基础上的测试,设计师一旦描绘出一个稳定的逻辑级别安排,就可以将其应用于相关元素,以确定其项目是否符合每个规范形式。然后,如果需要,设计者会采取适当的纠正措施。

冗余

在关系模型中,虽然列中包含的值的重复不仅是可以接受的而且是预期的,但禁止重复的行。就此而言,据我所知,在之前公开的逻辑布局中包含的所有表中都防止了重复行和其他类型的有害冗余,也许您想澄清您在这方面的担忧。

无论如何,您当然可以 (a) 通过范式对您自己的所述结构进行评估,以定义它是否满足要求,以及 (b) 在必要时对其进行修改。

相关资源

  • 本系列文章中,我提出了一些关于可以将两种不同实体类型的实例相互关联的简单 M:N 关联的讨论。
  • 另一篇文章中,我提出了一种处理“物料清单”或“零件爆炸”结构出现的方法,其中我描述了如何连接同一类型实体的不同实例。

三元关联

您通过评论提出了另一个重要方面(发布在现已删除的答案中):

每次我尝试建造一座桥梁时,这座桥梁中的元素也有一个多对多的关系,我的印象是不允许或至少不鼓励。

这种情况似乎表明您关注的问题之一与概念三元关联有关。基本上,这种关联发生在存在 (1) 涉及 (2) 两个其他关系的关系时,换句话说,“关系之间的关系”——也是一种典型情况,因为关系本身就是一个实体——。

这些安排如果管理得当,也不会带来有害的冗余。而且,是的,如果在某个用例中,您确定此类关系出现在“现实世界”实体类型之间,则您必须 (i) 建模并 (ii) 在逻辑级别准确地声明它们。


归档时间:

查看次数:

4905 次

最近记录:

6 年,2 月 前