设计一个友谊数据库结构:我应该使用多值列吗?


9

假设我有一个名为的表User_FriendList,它具有以下特征:

CREATE TABLE User_FriendList (
    ID ...,
    User_ID...,
    FriendList_IDs...,
    CONSTRAINT User_Friendlist_PK PRIMARY KEY (ID)
);

让我们假设该表包含以下数据:

 + ---- + --------- + --------------------------- +
 | ID | 用户名 | Friendlist_IDs             |
 + ---- + --------- + --------------------------- +
 | 1 | 102 | 2:15:66:35:26:17:|
 + ---- + --------- + --------------------------- +
 | 2 | 114 | 1:12:63:33:24:16:102 |
 + ---- + --------- + --------------------------- +
 | 3 | 117 | 6:24:52:61:23:90:97:118 |
 + ---- + --------- + --------------------------- +

注意:在PHP中爆炸为时,“:”(冒号)是定界符array

问题

所以:

  • 这是一个方便的方式来“存储”的IDsFriendList

  • 或者,相反,我是否应该在各个行FriendId中每个行中只有一个单一值,并且当我需要检索给定列表的所有行时,只需执行类似的查询SELECT * FROM UserFriendList WHERE UserId = 1



只要您不打算对ID进行任何操作并且不特别在意数据质量,这是一种存储ID的简便方法。
mustaccio

我认为codedodle.com/2014/12/social-network-friends-database.html可能是最好的解决方案之一。
古普塔

Answers:


19

管理单个信息

假设在您的业务领域,

  • 一个用户可以有0-1或-许多朋友 ;
  • 朋友必须首先被登记为用户 ; 和
  • 您将搜索和/或添加,和/或删除和/或修改“ 朋友列表”的单个值;

那么,在多值列中收集的每个特定数据都Friendlist_IDs代表一条带有非常精确含义的单独信息。因此,说专栏

  • 需要一组适当的显式约束,并且
  • 它的值具有通过几种关系操作(或其组合)进行单独操纵的潜力。

简短答案

因此,您应将Friendlist_IDs(a)列中的每个值都保留在(b)表中,该列仅接受每行一个唯一值,该表代表可以在用户之间发生的概念级别关联类型,即,友谊 -如我将在以下各节中举例说明。

这样,您将能够(i)将表作为数学关系处理,并将(ii)将列作为数学关系属性进行处理 -当然,这与MySQL及其SQL方言所允许的程度一样。

为什么?

因为由E.F. Codd博士创建的数据关系模型需要具有由列组成的表,而这些表恰好每行拥有一个适用类型的值;因此,声明一个表中的列可以包含所讨论的域或类型的多个值(1)不表示数学关系,而(2)将不允许获得上述理论框架中提出的优点。

建立用户之间的友谊模型:首先定义业务环境规则

我强烈建议根据相关业务规则的定义(首先要定义相应的概念模式)来塑造数据库,该业务规则除其他因素外还必须描述感兴趣的不同方面之间存在的相互关系的类型,即,适用的实体类型及其性质 ; 例如:

  • 一个用户主要是由他或她的识别用户ID
  • 一个用户交替由他或她的组合标识的名字姓氏性别生日
  • 一个用户是通过交替他或她的识别用户名
  • 用户请求者的零一或一对多友谊
  • 用户收件人的零一或一对多友谊
  • 友谊主要由其的组合来标识RequesterId及其AddresseeId

信息库IDEF1X图表

通过这种方式,我能够得出图1所示的IDEF1X 1图,该图集成了先前制定的大多数规则:

图1.用户友谊IDEF1X图

如图所示,请求者收件人是表示由参加给定“ 友谊”的特定用户执行的角色的表示。

既如此,该友谊实体类型描绘的关联类型多对许多(M:N)比基数可以涉及不同的的ocurrences 相同的实体类型,即用户。这样,它就是被称为“材料清单”或“零件爆炸”的经典构造的示例。


1 信息建模集成定义 IDEF1X)是一项高度值得推荐的技术,它是由美国国家标准技术研究院(NIST)于1993年12月建立为标准的。它扎实地基于(a)关系模型的唯一发起者(即 EF Codd博士)撰写的早期理论材料;(b)由陈PP博士开发的数据的实体关系视图;以及(c)Robert G. Brown创建的逻辑数据库设计技术。


说明性的SQL-DDL逻辑设计

然后,从上面显示的IDEF1X图表中,声明一个DDL安排(如下所示)更加“自然”:

-- You should determine which are the most fitting 
-- data types and sizes for all the table columns 
-- depending on your business context characteristics.

-- At the physical level, you should make accurate tests 
-- to define the mostconvenient INDEX strategies based on 
-- the pertinent query tendencies.

-- As one would expect, you are free to make use of 
-- your preferred (or required) naming conventions. 

CREATE TABLE UserProfile ( -- Represents an independent entity type.
    UserId          INT      NOT NULL,
    FirstName       CHAR(30) NOT NULL,
    LastName        CHAR(30) NOT NULL,
    BirthDate       DATE     NOT NULL,
    GenderCode      CHAR(3)  NOT NULL,
    Username        CHAR(20) NOT NULL,
    CreatedDateTime DATETIME NOT NULL,
    --
    CONSTRAINT UserProfile_PK  PRIMARY KEY (UserId),
    CONSTRAINT UserProfile_AK1 UNIQUE ( -- Composite ALTERNATE KEY.
        FirstName,
        LastName,
        GenderCode,
        BirthDate
    ),
    CONSTRAINT UserProfile_AK2 UNIQUE (Username) -- Single-column ALTERNATE KEY.
);

CREATE TABLE Friendship ( -- Stands for an associative entity type.
    RequesterId     INT      NOT NULL,
    AddresseeId     INT      NOT NULL, -- Fixed with a well-delimited data type.
    CreatedDateTime DATETIME NOT NULL,
    --
    CONSTRAINT Friendship_PK            PRIMARY KEY (RequesterId, AddresseeId), -- Composite PRIMARY KEY.
    CONSTRAINT FriendshipToRequester_FK FOREIGN KEY (RequesterId)
        REFERENCES UserProfile (UserId),
    CONSTRAINT FriendshipToAddressee_FK FOREIGN KEY (AddresseeId)
        REFERENCES UserProfile (UserId)
);

以这种方式:

  • 每个基本表代表一个单独的实体类型;
  • 代表相应实体类型的唯一属性;
  • 固定一个特定的数据类型a,以确保它包含的所有都属于一个特定且定义明确的集合,例如INT,DATETIME,CHAR等;和
  • 为了确保所有表中保留的形式的断言满足在概念性模式中确定的业务规则,配置了(声明性地)多个约束b

单值列的优点

如所示,您可以例如:

  • 利用数据库管理系统(为简洁起见,为DBMS)强制执行的参照完整性Friendship.AddresseeId,因为将其约束为对列进行引用的FOREIGN KEY(为简洁起见,为FK)可以UserProfile.UserId确保每个值都指向一个现有行。

  • 创建由列组合组成的复合主键(PK)(Friendship.RequesterId, Friendship.AddresseeId),有助于优雅地区分所有INSERTed行,并自然保护其唯一性

    当然,这意味着为系统分配的代理值(例如,使用Microsoft SQL Server中的IDENTITY属性或MySQL中的AUTO_INCREMENT属性设置的附加列)和辅助INDEX 的附加完全是多余的

  • 将保留的值限制Friendship.AddresseeId为精确的数据类型c(应匹配,例如,为UserProfile.UserId,在本例中为INT建立的类型),从而让DBMS负责相关的自动验证。

    该因素还可以帮助(a)利用相应的内置类型函数,以及(b)优化磁盘空间使用。

  • 通过为该列配置小型且快速的从属索引来优化物理级别的数据检索,因为这些物理元素可以极大地帮助加快涉及该列的查询。Friendship.AddresseeId

    当然,例如,您可以单独设置一个单列INDEX ,或者Friendship.AddresseeId包含Friendship.RequesterId和和Friendship.AddresseeId或同时包含这两个的多列。

  • 避免通过“搜索”在同一列内收集在一起的不同值(很可能重复,错误键入等)而引入不必要的复杂性,因为这样做会最终减慢系统的运行速度,因为您会必须诉诸于资源和耗时的非关系方法来完成上述任务。

因此,出于多种原因,需要仔细分析相关的业务环境,以便准确地标记出每个表列的类型d

正如所阐明的那样,数据库设计者所扮演的角色对于充分利用(1)关系模型提供的逻辑级别的好处和(2)所选DBMS提供的物理机制至关重要。


abcd显然,当使用支持DOMAIN创建(独特的关系功能)的SQL平台(例如 Firebird PostgreSQL)时,您可以声明仅接受属于它们各自值的列(适当地受约束,有时共享)DOMAIN。


共享正在考虑的数据库的一个或多个应用程序

当你聘请arrays的应用程序(一个或多个)accesing数据库的代码,你只需要检索相关数据集(S)完全然后“绑定”它(们)的有关代码结构或执行相关的应用程序流程。

单值列的其他好处:数据库结构扩展更加容易

AddresseeId数据点保存在其保留的类型正确的列中的另一个优点是,它极大地方便了数据库结构的扩展,下面我将举例说明。

方案进展:纳入“ 友谊状态”概念

由于友谊会随着时间的推移而发展,因此您可能必须跟踪这种现象,因此,您将必须(i)扩展概念性架构,并且(ii)在逻辑布局中声明更多的表。因此,让我们安排下一个业务规则来描述新的公司:

  • 一个朋友拥有一个一对多FriendshipStatuses
  • FriendshipStatus主要由其的组合来标识RequesterId,其AddresseeId及其SpecifiedDateTime
  • 用户指定零酮或一对多FriendshipStatuses
  • 一个状态进行分类零一或一对多FriendshipStatuses
  • 一个状态主要由其确定的StatusCode
  • 一个状态交替地通过它的标识名称

扩展的IDEF1X图

继而,可以扩展先前的IDEF1X图,以包括上述新的实体类型和相互关系类型。图2中显示了描述与新元素相关联的先前元素的

图2.友谊状态IDEF1X图表

逻辑结构添加

之后,我们可以使用以下声明来延长DDL布局:

--
CREATE TABLE MyStatus ( -- Denotes an independent entity type.
    StatusCode CHAR(1)  NOT NULL,
    Name       CHAR(30) NOT NULL,
    --
    CONSTRAINT MyStatus_PK PRIMARY KEY (StatusCode),
    CONSTRAINT MyStatus_AK UNIQUE      (Name) -- ALTERNATE KEY.
); 

CREATE TABLE FriendshipStatus ( -- Represents an associative entity type.
    RequesterId       INT      NOT NULL,
    AddresseeId       INT      NOT NULL,
    SpecifiedDateTime DATETIME NOT NULL,
    StatusCode        CHAR(1)  NOT NULL,
    SpecifierId       INT      NOT NULL,
    --
    CONSTRAINT FriendshipStatus_PK             PRIMARY KEY (RequesterId, AddresseeId, SpecifiedDateTime), -- Composite PRIMARY KEY.
    CONSTRAINT FriendshipStatusToFriendship_FK FOREIGN KEY (RequesterId, AddresseeId)
        REFERENCES Friendship  (RequesterId, AddresseeId), -- Composite FOREIGN KEY.
    CONSTRAINT FriendshipStatusToMyStatus_FK   FOREIGN KEY (StatusCode)
        REFERENCES MyStatus    (StatusCode),
    CONSTRAINT FriendshipStatusToSpecifier_FK  FOREIGN KEY (SpecifierId)
        REFERENCES UserProfile (UserId)      
);

因此,每次需要更新给定友谊状态时,用户只需插入新行,其中包含:FriendshipStatus

  • 从相关行中获取的适当值RequesterIdAddresseeIdFriendship

  • StatusCode从中汲取的新的有意义的价值MyStatus.StatusCode

  • 准确的INSERTion即时,即SpecifiedDateTime-最好使用服务器功能,以便您可以可靠的方式检索和保留它- 和

  • SpecifierId值将指示相应的值(理想情况下UserIdFriendshipStatus借助您的应用功能)将新代码输入到系统中。

在此程度上,让我们假设该MyStatus表包含以下数据-PK值(a)最终用户,应用程序程序员和DBA友好,并且(b)在物理实现级别的字节方面小而又快—:

 + -————————————- + -————————
 | StatusCode | 姓名       |
 + -————————————- + -————————
 | R | 要求|
 + ------------ + ----------- +
 | A | 接受|
 + ------------ + ----------- +
 | D | 拒绝|
 + ------------ + ----------- +
 | B | Bloqued |
 + ------------ + ----------- +

因此,该FriendshipStatus表可能包含如下所示的数据:

 + -——————————————- + -————————————- + -———— ———- + -————————————- + -————————————- +
 | RequesterId | AddresseeId | SpecifiedDateTime        | StatusCode | SpecifierId |
 + -——————————————- + -————————————- + -———— ———- + -————————————- + -————————————- +
 | 1750 | 1748 | 2016-04-01 16:58:12.000 | R | 1750 |
 + ------------- + ------------- + --------------------- ---- + ------------ + ------------- +
 | 1750 | 1748 | 2016-04-02 09:12:05.000 | A | 1748 |
 + ------------- + ------------- + --------------------- ---- + ------------ + ------------- +
 | 1750 | 1748 | 2016-04-04 10:57:01.000 | B | 1750 |
 + ------------- + ------------- + --------------------- ---- + ------------ + ------------- +
 | 1750 | 1748 | 2016-04-07 07:33:08.000 | R | 1748 |
 + ------------- + ------------- + --------------------- ---- + ------------ + ------------- +
 | 1750 | 1748 | 2016-04-08 12:12:09.000 | A | 1750 |
 + ------------- + ------------- + --------------------- ---- + ------------ + ------------- +

如您所见,可以说该FriendshipStatus表用于组成时间序列


相关职位

您可能还对以下内容感兴趣:

  • 在这个答案中,我提出了一种基本方法来处理两个不同实体类型之间的常见多对多关系。
  • 图1所示的IDEF1X图说明了另一个答案。要特别注意名为MarriageProgeny的实体类型,因为它们是如何处理“零件爆炸问题”的另外两个示例。
  • 这篇文章简要介绍了如何在一个列中保存不同的信息。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.