从概念的角度分析场景(呈现与称为时间数据库的主题相关的特征),可以确定:(a)“当前” 博客故事版本和(b)“过去” 博客故事版本,尽管非常类似,是不同类型的实体。
除此之外,在逻辑抽象级别上工作时,必须将不同种类的事实(用行表示)保留在不同的表中。在考虑的情况下,即使非常相似,(i)关于“当前” 版本的事实也与(ii)关于“过去” 版本的事实不同。
因此,我建议通过两个表来管理情况:
每个(1)列的数量略有不同,(2)不同的约束组。
回到概念层,我认为-在您的业务环境中- 作者和编辑者是可以被描述为可由用户扮演的角色的概念,而这些重要方面取决于数据派生(通过逻辑级别的操作)和解释(由Blog Stories的读者和作家在计算机信息系统的外部级别借助一个或多个应用程序进行)。
我将在下面详细介绍所有这些因素和其他相关要点。
商业规则
根据我对您的需求的理解,以下业务规则公式(根据相关实体类型及其相互关系的种类放在一起)对于建立相应的概念架构特别有用:
- 一个用户写道零一或一对多BlogStories
- 一个BlogStory持有零一或一对多BlogStoryVersions
- 一个用户写了一个零个或多个BlogStoryVersions
信息库IDEF1X图表
因此,为了借助于图形装置来解释我的建议,我已经创建了一个样本IDEF1X 一个是从上面配制的商业规则和其他特征,似乎相关衍生图。如图1所示:
为什么将BlogStory和BlogStoryVersion概念化为两种不同的实体类型?
因为:
一个 用于信息建模集成定义( IDEF1X)是被确立为一个非常可取的数据建模技术标准是由美国在1993年12月美国国家标准与技术研究院(NIST)。它基于关系模型的唯一发起人(即 EF Codd博士)撰写的早期理论材料。陈PP博士开发的关于数据的实体关系视图;以及由Robert G. Brown创建的逻辑数据库设计技术。
说明性逻辑SQL-DDL布局
然后,基于先前提出的概念分析,我在下面声明了逻辑级别的设计:
-- You should determine which are the most fitting
-- data types and sizes for all your table columns
-- depending on your business context characteristics.
-- Also you should make accurate tests to define the most
-- convenient index strategies at the physical level.
-- As one would expect, you are free to make use of
-- your preferred (or required) naming conventions.
CREATE TABLE UserProfile (
UserId INT NOT NULL,
FirstName CHAR(30) NOT NULL,
LastName CHAR(30) NOT NULL,
BirthDate DATETIME 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,
BirthDate,
GenderCode
),
CONSTRAINT UserProfile_AK2 UNIQUE (UserName) -- ALTERNATE KEY.
);
CREATE TABLE BlogStory (
BlogStoryNumber INT NOT NULL,
Title CHAR(60) NOT NULL,
Content TEXT NOT NULL,
CoverImageName CHAR(30) NOT NULL,
IsActive BIT(1) NOT NULL,
AuthorId INT NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT BlogStory_PK PRIMARY KEY (BlogStoryNumber),
CONSTRAINT BlogStory_AK UNIQUE (Title), -- ALTERNATE KEY.
CONSTRAINT BlogStoryToUserProfile_FK FOREIGN KEY (AuthorId)
REFERENCES UserProfile (UserId)
);
CREATE TABLE BlogStoryVersion (
BlogStoryNumber INT NOT NULL,
CreatedDateTime DATETIME NOT NULL,
Title CHAR(60) NOT NULL,
Content TEXT NOT NULL,
CoverImageName CHAR(30) NOT NULL,
IsActive BIT(1) NOT NULL,
AuthorId INT NOT NULL,
UpdatedDateTime DATETIME NOT NULL,
--
CONSTRAINT BlogStoryVersion_PK PRIMARY KEY (BlogStoryNumber, CreatedDateTime), -- Composite PK.
CONSTRAINT BlogStoryVersionToBlogStory_FK FOREIGN KEY (BlogStoryNumber)
REFERENCES BlogStory (BlogStoryNumber),
CONSTRAINT BlogStoryVersionToUserProfile_FK FOREIGN KEY (AuthorId)
REFERENCES UserProfile (UserId),
CONSTRAINT DatesSuccession_CK CHECK (UpdatedDateTime > CreatedDateTime) --Let us hope that MySQL will finally enforce CHECK constraints in a near future version.
);
在运行于MySQL 5.6的SQL Fiddle中进行了测试。
该BlogStory
表
如您在演示设计中所见,我已经BlogStory
使用INT数据类型定义了PRIMARY KEY(为简便起见,PK)列。在这方面,您可能想修复内置的自动过程,该过程会在每次插入行时为此类列生成并分配一个数值。如果您不介意在这组值中偶尔留下空白,那么可以使用MySQL环境中常用的AUTO_INCREMENT属性。
输入所有单独的BlogStory.CreatedDateTime
数据点时,可以使用NOW()函数,该函数在确切的INSERT操作瞬间返回数据库服务器中当前的日期和时间值。对我来说,这种实践绝对比使用外部例程更合适,而且更不容易出错。
就像在(现在删除的)注释中所讨论的那样,如果您希望避免维护BlogStory.Title
重复值的可能性,则必须为此列设置UNIQUE约束。由于给定标题可以由几个(甚至全部)“过去”共享BlogStoryVersions,然后UNIQUE约束应该不会被用于建立BlogStoryVersion.Title
列。
如果您需要提供“软”或“逻辑” DELETE功能,我会列出BIT(1)BlogStory.IsActive
类型的列(尽管也可以使用TINYINT)。
有关BlogStoryVersion
表格的详细信息
另一方面,BlogStoryVersion
表的PK 由(a)BlogStoryNumber
和(b)列组成,该列CreatedDateTime
当然标记了BlogStory
行经过INSERT 的确切时刻。
BlogStoryVersion.BlogStoryNumber
除作为PK的一部分外,还被约束为reference的FOREIGN KEY(FK)BlogStory.BlogStoryNumber
,该配置在这两个表的行之间强制实施了引用完整性。在这方面,BlogStoryVersion.BlogStoryNumber
不需要自动生成a,因为将其设置为FK时,必须从已包含在相关BlogStory.BlogStoryNumber
副本中的值中“提取”插入此列的值。
BlogStoryVersion.UpdatedDateTime
如预期的那样,该列应保留BlogStory
修改行并因此将其添加到BlogStoryVersion
表中的时间点。因此,您也可以在这种情况下使用NOW()函数。
所述区间之间理解BlogStoryVersion.CreatedDateTime
和BlogStoryVersion.UpdatedDateTime
表达整个时期,在此期间一个BlogStory
行是“存在”或“电流”。
Version
列注意事项
它可以认为是有用的BlogStoryVersion.CreatedDateTime
作为保持代表一个特定的“过去”值的列版本 A的BlogStory。我认为这比a VersionId
或更为有益VersionCode
,因为从某种意义上说,它倾向于用户友好,因为人们往往更熟悉时间概念。例如,博客作者或读者可以通过类似于以下方式引用BlogStoryVersion:
- “我希望看到具体的版本中的BlogStory通过识别号码
1750
这是创建于26 August 2015
在9:30
”。
该作者与编辑的角色:数据推导和解释
通过这种方法,可以很容易地分辨谁拥有“原始”的AuthorId
一个具体的BlogStory选择“最早的” 版本有一定的BlogStoryId
自BlogStoryVersion
凭借应用的表MIN()函数来BlogStoryVersion.CreatedDateTime
。
这样,所有 “以后”或“成功” 版本行中BlogStoryVersion.AuthorId
包含的每个值自然会指示出相应版本的作者标识符,但是也可以说,该值同时表示由相关用户担任BlogStory的“原始” 版本的编辑者所扮演的角色。
是的,一个给定的AuthorId
值可以由多BlogStoryVersion
行共享,但这实际上是一条信息,可以告诉每个Version非常重要的信息,因此重复上述数据就不是问题。
DATETIME列的格式
至于DATETIME数据类型,是的,您是对的,“ MySQL检索并以' YYYY-MM-DD HH:MM:SS
'format格式显示DATETIME值,但是您可以放心地以这种方式输入相关数据,而当您必须执行查询时,只需利用内置的DATE和TIME函数,除其他外,以适合您的用户的格式显示相关值。或者,您当然可以通过您的应用程序代码执行这种数据格式化。
BlogStory
UPDATE操作的含义
每次BlogStory
一行发生UPDATE时,您必须确保在修改发生之前将“存在”的相应值插入BlogStoryVersion
表中。因此,我强烈建议在一次酸交易中完成这些操作,以确保将它们视为不可分割的工作单元。您也可以雇用TRIGGERS,但是可以这样说,它们会使事情变得不整洁。
介绍VersionId
或VersionCode
列
如果您出于业务状况或个人喜好而选择合并BlogStory.VersionId
或BlogStory.VersionCode
列来区分BlogStoryVersions,则应考虑以下可能性:
VersionCode
在(i)整个BlogStory
表格以及(ii)中可能要求A 为唯一BlogStoryVersion
。
因此,您必须实施经过仔细测试且完全可靠的方法,以便生成和分配每个Code
值。
也许这些VersionCode
值可以在不同的BlogStory
行中重复,但绝不能与同一个重复BlogStoryNumber
。例如,您可能有:
- 一个BlogStoryNumber
3
-版本83o7c5c
,同时,
- 一个BlogStoryNumber
86
-版本83o7c5c
和
- 一个BlogStoryNumber
958
-版本83o7c5c
。
后面的可能性开辟了另一种选择:
保持一个VersionNumber
对BlogStories
,所以有可能是:
- BlogStoryNumber-
23
版本1, 2, 3…
;
- BlogStoryNumber-
650
版本1, 2, 3…
;
- BlogStoryNumber-
2254
版本1, 2, 3…
;
- 等等
在一个表中保存“原始”和“后续”版本
虽然维护所有BlogStoryVersions在同一个人基地台是可能的,我建议不要做,因为你会被混合两种截然不同的(概念)类型的事实,因此,其对不良副作用的
- 数据约束和操作(在逻辑级别),以及
- 相关处理和存储(在物理层)。
但是,如果您选择遵循该行动方案,则仍然可以利用上面详述的许多想法,例如:
- 一个复合 PK由一个INT柱(的
BlogStoryNumber
)和DATETIME柱(CreatedDateTime
);
- 服务器功能的使用,以优化相关流程,以及
- 的作者和编辑器导出角色。
看到,通过采用这种方法,BlogStoryNumber
将在添加“较新” 版本后立即复制一个值,并且您可以评估的一个选项(与上一节中提到的选项非常相似)正在建立PK列组成,并以这种方式,你将能够唯一标识每个版本 A的BlogStory。你还可以用组合尝试和太。BlogStory
BlogStoryNumber
VersionCode
BlogStoryNumber
VersionNumber
类似情况
您可能会找到我对这个帮助问题的答案,因为我还建议在相关数据库中启用临时功能以处理类似的情况。