管理单个信息
假设在您的业务领域,
- 一个用户可以有0-1或-许多朋友 ;
- 一朋友必须首先被登记为用户 ; 和
- 您将搜索和/或添加,和/或删除和/或修改“ 朋友列表”的单个值;
那么,在多值列中收集的每个特定数据都Friendlist_IDs
代表一条带有非常精确含义的单独信息。因此,说专栏
- 需要一组适当的显式约束,并且
- 它的值具有通过几种关系操作(或其组合)进行单独操纵的潜力。
简短答案
因此,您应将Friendlist_IDs
(a)列中的每个值都保留在(b)表中,该列仅接受每行一个唯一值,该表代表可以在用户之间发生的概念级别关联类型,即,友谊 -如我将在以下各节中举例说明。
这样,您将能够(i)将表作为数学关系处理,并将(ii)将列作为数学关系属性进行处理 -当然,这与MySQL及其SQL方言所允许的程度一样。
为什么?
因为由E.F. Codd博士创建的数据关系模型需要具有由列组成的表,而这些表恰好每行拥有一个适用域或类型的值;因此,声明一个表中的列可以包含所讨论的域或类型的多个值(1)不表示数学关系,而(2)将不允许获得上述理论框架中提出的优点。
建立用户之间的友谊模型:首先定义业务环境规则
我强烈建议根据相关业务规则的定义(首先要定义相应的概念模式)来塑造数据库,该业务规则除其他因素外还必须描述感兴趣的不同方面之间存在的相互关系的类型,即,适用的实体类型及其性质 ; 例如:
- 一个用户主要是由他或她的识别用户ID
- 一个用户交替由他或她的组合标识的名字,姓氏,性别和生日
- 一个用户是通过交替他或她的识别用户名
- 甲用户是请求者的零一或一对多友谊
- 甲用户是收件人的零一或一对多友谊
- 甲友谊主要由其的组合来标识RequesterId及其AddresseeId
信息库IDEF1X图表
通过这种方式,我能够得出图1所示的IDEF1X 1图,该图集成了先前制定的大多数规则:
如图所示,请求者和收件人是表示由参加给定“ 友谊”的特定用户执行的角色的表示。
既如此,该友谊实体类型描绘的关联类型多对许多(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提供的物理机制至关重要。
a,b,c和d显然,当使用支持DOMAIN创建(独特的关系功能)的SQL平台(例如 Firebird和 PostgreSQL)时,您可以声明仅接受属于它们各自值的列(适当地受约束,有时共享)DOMAIN。
共享正在考虑的数据库的一个或多个应用程序
当你聘请arrays
的应用程序(一个或多个)accesing数据库的代码,你只需要检索相关数据集(S)完全然后“绑定”它(们)的有关代码结构或执行相关的应用程序流程。
单值列的其他好处:数据库结构扩展更加容易
将AddresseeId
数据点保存在其保留的类型正确的列中的另一个优点是,它极大地方便了数据库结构的扩展,下面我将举例说明。
方案进展:纳入“ 友谊状态”概念
由于友谊会随着时间的推移而发展,因此您可能必须跟踪这种现象,因此,您将必须(i)扩展概念性架构,并且(ii)在逻辑布局中声明更多的表。因此,让我们安排下一个业务规则来描述新的公司:
- 一个朋友拥有一个一对多FriendshipStatuses
- 甲FriendshipStatus主要由其的组合来标识RequesterId,其AddresseeId及其SpecifiedDateTime
- 甲用户指定零酮或一对多FriendshipStatuses
- 一个状态进行分类零一或一对多FriendshipStatuses
- 一个状态主要由其确定的StatusCode
- 一个状态交替地通过它的标识名称
扩展的IDEF1X图
继而,可以扩展先前的IDEF1X图,以包括上述新的实体类型和相互关系类型。图2中显示了描述与新元素相关联的先前元素的图:
逻辑结构添加
之后,我们可以使用以下声明来延长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
从相关行中获取的适当值RequesterId
和AddresseeId
值Friendship
;
StatusCode
从中汲取的新的有意义的价值MyStatus.StatusCode
;
准确的INSERTion即时,即SpecifiedDateTime
-最好使用服务器功能,以便您可以可靠的方式检索和保留它- 和
该SpecifierId
值将指示相应的值(理想情况下UserId
,FriendshipStatus
借助您的应用功能)将新代码输入到系统中。
在此程度上,让我们假设该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图说明了另一个答案。要特别注意名为Marriage和Progeny的实体类型,因为它们是如何处理“零件爆炸问题”的另外两个示例。
- 这篇文章简要介绍了如何在一个列中保存不同的信息。