创建包含阻止好友选项的友谊数据库

Iva*_*iro 3 mysql database-design

我的数据库老师要求我们使用 MySQL 作为数据库管理系统编写 Facebook 的克隆,所以我试图在相应的数据库中表示朋友关系。

到目前为止,我已经通过到处搜索构建了以下表格:

CREATE TABLE Users (
    UserID INT UNSIGNED NOT NULL AUTO_INCREMENT,
    Nome VARCHAR (20) NOT NULL,
    Sobrenome VARCHAR (20) NOT NULL,
    Foto LONGBLOB,
    Cidade VARCHAR (180),
    PRIMARY KEY (UserID)
);

CREATE TABLE Friends (
    UserID INT UNSIGNED NOT NULL,
    FriendID INT UNSIGNED NOT NULL,
    PRIMARY KEY (UserID, FriendID),
    CONSTRAINT FK_UserID
    FOREIGN KEY UserID REFERENCES Users (UserID),
    CONSTRAINT FK_FriendID
    FOREIGN KEY FriendID REFERENCES Users (UserID)
); 
Run Code Online (Sandbox Code Playgroud)

我将Friends表描述为使用UserID(声明为Users表主键)的自关系。

除了一些基本的#1064 错误,我正在处理:是否可以为这种关系添加一个阻止好友选项?例如,如果我不想再与某人交谈,我只是阻止他们。

MDC*_*CCL 9

我将涵盖“块朋友”的要求引入的概念友谊状态至系统,其中“阻止”是唯一的可能的实例中的一个,因为友谊也可以是为,例如,“已接受”或“拒绝” - , -我将详细说明如下。

商业规则

在对关系数据库建模时,我强烈建议 (a) 确定和 (b) 制定适用于业务环境的所有相关信息特征的规则,并(c) 创建表和声明约束之前执行此操作。通过这种方式,您可以以所需的精度描绘相应的概念模式,然后逻辑级别的声明就更容易和自然了。

因此,让我们从Friendships系统的一些基本说明规则开始:

  • 用户请求者的零一或一对多友谊
  • 用户收件人的零一或一对多友谊
  • 一个Friendship主要由它的RequesterId和它的AddresseeId的组合来标识

然后状态方面发挥作用:

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

说明性IDEF1X图

在准确地制定了所有业务规则后,您可以绘制一个 IDEF1X一个类似于图 1 中阐述的图表来提供图形通信设备:

图 1 - 友谊和状态 IDEF1X 图

如图所示,RequesterAddressee是表示参与给定Friendship 的每个User 所执行的特定角色的标签。

友谊实体类型描绘具有的关联多对许多(M:N)基数比率可涉及不同的事件相同的实体类型,即用户。这是称为“物料清单”或“零件爆炸”的经典概念构造的一个示例。

StatusFriendship通过FriendshipStatus实体类型连接,该实体类型描绘了可能成为所涉及Friendship实例的Status规范序列的内容。


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


说明性的逻辑 SQL-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)
);

--
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)      
);
Run Code Online (Sandbox Code Playgroud)


在此 SQL Fiddle 中在 MySQL 5.6 上进行了测试。


一个Friendship行和其最早的FriendshipStatus相关对口

在这种安排下,当用户请求Friendship 时,相应的Friendship行必须与互补的对应行一起插入,相应的行FriendshipStatus必须包含一个值 'R' in StatusCode,表示用户只是请求了Friendship,并且正在等待收件人接受它 ('A')、拒绝它 ('D') 或按要求保留它。

像往常一样,我会果断地设置多行操作,这些操作必须在ACID TRANSACTIONS的帮助下作为单个工作单元执行。不用说,访问数据库的应用程序组件的帮助对于促进友谊的处理也是非常有帮助的。

后面的FriendshipStatus

随后,每次必须更新某个友谊状态时,用户只需插入一个新行,其中包含:FriendshipStatus

  • 各自的RequesterIdAddresseeId值——取自相关Friendship行——;

  • 新的和有意义的StatusCode价值MyStatus.StatusCode——取自——;

  • 确切的 INSERTion 瞬间的值,即SpecifiedDateTime——最好使用服务器功能,以便您可以以可靠的方式检索和保留它——;和

  • SpecifierId将表明各自的值UserId是进入新FriendshipStatus到系统中。

样本数据

在这种情况下,让我们假设该MyStatus表充当“查找”角色,包含以下数据 - PK 值是 (1) 最终用户、应用程序程序员和 DBA 友好和 (2) 小并且在物理实现级别的字节数方面很快—— :

 +-———————————-+-—————————-+
 | 状态码| 姓名      |
 +-———————————-+-—————————-+
 | R | 要求 |
 +------------+------------+
 | 一个 | 接受 |
 +------------+------------+
 | D | 拒绝 |
 +------------+------------+
 | 乙 | 已阻止 |
 +------------+------------+

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

 +-————————————-+-————————————-+-————————————————————— ————+-—————————————-+-————————————-+
 | 请求者 ID | 收件人ID | 指定日期时间       | 状态码| 说明符 ID |
 +-————————————-+-————————————-+-————————————————————— ————+-—————————————-+-————————————-+
 | 1750 | 1748 | 2018-04-01 16:58:12.000 | R | 1750 |
 +------------+------------+------------ ----+------------+------------+
 | 1750 | 1748 | 2018-04-02 09:12:05.000 | 一个 | 1748 |
 +------------+------------+------------ ----+------------+------------+
 | 1750 | 1748 | 2018-04-04 10:57:01.000 | 乙 | 1750 |
 +------------+------------+------------ ----+------------+------------+
 | 1750 | 1748 | 2018-04-07 07:33:08.000 | R | 1748 |
 +------------+------------+------------ ----+------------+------------+
 | 1750 | 1748 | 2018-04-08 12:12:09.000 | 一个 | 1750 |
 +------------+------------+------------ ----+------------+------------+

可以说这张FriendshipStatus表的目的是组成一个时间序列,代表友谊的演变。

您可以明确地声明一个或多个包含来自多个表的列的视图,以方便例如编写用于数据检索的代码。

阻止一个朋友

FriendshipStatus表的示例数据所示,列中包含值 'B' 的StatusCode行表示某个用户决定阻止他或她的一个Friends

并且,是的,您可以使用您的应用程序组件来访问数据库并检查最新 FriendshipStatus行(通过MAX(SpecifiedDateTime))的给定(RequesterId, AddresseId)值组合,以控制系统适用进程的流程。

其他选项:仅保留最近的状态

如果出于某种原因,您必须避免以前面描述的方式保留FriendShipStatus历史记录,您可以放弃创建FriendshipStatus表并简单地设置,Friendship以便它包含Status.StatusCode直接引用的 FOREIGN KEY 约束:

CREATE TABLE Friendship (
    RequesterId    INT      NOT NULL,
    AddresseeId    INT      NOT NULL,
    CratedDateTime DATETIME NOT NULL,
    StatusCode     CHAR(1)  NOT NULL,
    CreatorId      INT      NOT NULL,
    --
    CONSTRAINT Friendship_PK            PRIMARY KEY (RequesterId, AddresseeId),
    CONSTRAINT FriendshipToRequester_FK FOREIGN KEY (RequesterId)
        REFERENCES UserProfile (UserId),
    CONSTRAINT FriendshipToAddressee_FK FOREIGN KEY (AddresseeId)
        REFERENCES UserProfile (UserId)
    CONSTRAINT FriendshipToMyStatus_FK  FOREIGN KEY (StatusCode)
        REFERENCES MyStatus    (StatusCode),
    CONSTRAINT FriendshipToCreator_FK   FOREIGN KEY (CreatorId)
        REFERENCES UserProfile (UserId)      
);
Run Code Online (Sandbox Code Playgroud)


在此 SQL Fiddle 中在 MySQL 5.6 上进行了测试。


以这种方式,(i) 某Friendship行的 INSERT 与列中的值 'R' 对应StatusCode,每次修改友谊状态时,您只需要 (ii) 更新 中的值,并且-其中必须包含指定相关友谊状态的最新用户的标识符值—。StatusCodeSpecifiedDateTimeCreatorId

相关文章

这篇文章中提出的各种想法都基于这个问答,我在其中提出了一个满足类似业务环境信息需求的数据库设计。我建议阅读这些帖子,因为尽管该问题的重点与您的不同,但该答案更详细,并且包含几个相关点。