Vic*_*tor 17 mysql database-design
我知道这里和这里有人问过这个问题,但我有不同的可能实现的相同想法,我需要一些帮助。
最初,我的blogstories
表具有以下结构:
| Column | Type | Description |
|-----------|-------------|------------------------------------------------|
| uid | varchar(15) | 15 characters unique generated id |
| title | varchar(60) | story title |
| content | longtext | story content |
| author | varchar(10) | id of the user that originally wrote the story |
| timestamp | int | integer generated with microtime() |
Run Code Online (Sandbox Code Playgroud)
在我决定要为博客上的每个故事实施一些版本控制系统后,我想到的第一件事就是创建一个不同的表来保存编辑;在那之后,我想我可以修改现有的表来保存版本而不是edits。这是我想到的结构:
| Column | Type | Description |
|------------ |------------- |------------------------------------------------ |
| story_id | varchar(15) | 15 characters unique generated id |
| version_id | varchar(5) | 5 characters unique generated id |
| editor_id | varchar(10) | id of the user that commited |
| author_id | varchar(10) | id of the user that originally wrote the story |
| timestamp | int | integer generated with microtime() |
| title | varchar(60) | current story title |
| content | longtext | current story text |
| coverimg | varchar(20) | cover image name |
Run Code Online (Sandbox Code Playgroud)
我来这里的原因:
uid
初始表的字段在表中是唯一的。现在,它story_id
不再是独一无二的了。我该如何处理?(我以为我可以解决story_id = x
然后找到最新版本,但这似乎非常消耗资源,所以请提供您的建议)author_id
字段值在表的每一行中重复。我应该在哪里以及如何保存它?编辑
唯一码生成过程在CreateUniqueCode
函数中:
trait UIDFactory {
public function CryptoRand(int $min, int $max): int {
$range = $max - $min;
if ($range < 1) return $min;
$log = ceil(log($range, 2));
$bytes = (int) ($log / 8) + 1;
$bits = (int) $log + 1;
$filter = (int) (1 << $bits) - 1;
do {
$rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
$rnd = $rnd & $filter;
} while ($rnd >= $range);
return $min + $rnd;
}
public function CreateUID(int $length): string {
$token = "";
$codeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$codeAlphabet.= "abcdefghijklmnopqrstuvwxyz";
$codeAlphabet.= "0123456789";
$max = strlen($codeAlphabet) - 1;
for ($i=0; $i < $length; $i++) {
$token .= $codeAlphabet[$this->CryptoRand(0, $max)];
}
return $token;
}
}
Run Code Online (Sandbox Code Playgroud)
该代码是用Hack编写的,最初是由@Scott在他的回答中用PHP 编写的。
字段author_id
和editor_id
可以不同,因为有足够权限的用户可以编辑任何人的故事。
MDC*_*CCL 25
从概念的角度分析场景——它呈现与称为时间数据库的主题相关的特征——可以确定:(a)“现在”的博客故事版本和(b)“过去”的博客故事版本,尽管非常类似,是不同类型的实体。
除此之外,在逻辑抽象级别工作时,不同类型的事实(由行表示)必须保留在不同的表中。在所考虑的情况下,即使非常相似,(i) 关于“现在”版本的事实与 (ii) 关于“过去”版本的事实不同。
因此,我建议通过两个表来管理这种情况:
一个专门致力于为“当前”或“存在”版本的的博客故事,并
对于所有“以前”或“过去”版本,一个是独立的,但也与另一个相关联;
每个都有(1)稍微不同的列数和(2)不同的约束组。
回到概念层,我认为——在你的业务环境中——作者和编辑器是可以被描述为可由用户扮演的角色的概念,这些重要方面取决于数据推导(通过逻辑级操作操作)和解释(由博客故事的读者和作者在计算机化信息系统的外部级别在一个或多个应用程序的帮助下进行)。
我将在下面详细说明所有这些因素和其他相关要点。
根据我对您的需求的理解,以下业务规则制定(根据相关实体类型及其相互关系的类型放在一起)特别有助于建立相应的概念模式:
说明性IDEF1X图
因此,为了借助图形设备阐述我的建议,我创建了一个示例 IDEF1X,该图表源自上述制定的业务规则和其他似乎相关的功能。如图1所示:
为什么BlogStory和BlogStoryVersion 被概念化为两种不同的实体类型?
因为:
一个BlogStoryVersion实例(即“过去”之一)始终保持为一个值UpdatedDateTime财产,而BlogStory发生(即“存在”一个)从不保存它。
此外,这些类型的实体由两组不同属性的值唯一标识:BlogStoryNumber(在出现BlogStory 的情况下)和BlogStoryNumber加上CreatedDateTime(在BlogStoryVersion实例的情况下)。
一个 用于信息建模集成定义( IDEF1X)是被确立为一个非常可取的数据建模技术标准是由美国在1993年12月美国国家标准与技术研究院(NIST)。它是基于早期的理论材料撰写由独家发起的的关系模型,即EF科德博士; 关于数据的实体关系视图,由PP Chen 博士开发;以及由 Robert G. Brown 创建的逻辑数据库设计技术。
然后,根据之前介绍的概念分析,我在下面声明了逻辑级设计:
-- 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.
);
Run Code Online (Sandbox Code Playgroud)
在 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
列。
我包含了BIT(1)BlogStory.IsActive
类型的列(尽管也可以使用TINYINT),以防您需要提供“软”或“逻辑”删除功能。
BlogStoryVersion
表的详细信息
另一方面,BlogStoryVersion
表的 PK由 (a)BlogStoryNumber
和 (b) 一列组成,该列CreatedDateTime
当然标记了BlogStory
行经历 INSERT的精确时刻。
BlogStoryVersion.BlogStoryNumber
,除了作为 PK 的一部分之外,还被限制为引用 的外键 (FK) BlogStory.BlogStoryNumber
,该配置强制执行这两个表的行之间的引用完整性。在这方面,BlogStoryVersion.BlogStoryNumber
没有必要实现 a 的自动生成,因为设置为 FK,插入到此列中的值必须“从”已包含在相关BlogStory.BlogStoryNumber
对应项中的值“提取” 。
BlogStoryVersion.UpdatedDateTime
正如预期的那样,该列应保留BlogStory
修改行并因此添加到BlogStoryVersion
表中的时间点。因此,您也可以在这种情况下使用 NOW() 函数。
所述区间之间理解BlogStoryVersion.CreatedDateTime
和BlogStoryVersion.UpdatedDateTime
表达整个时期,在此期间一个BlogStory
行是“存在”或“电流”。
Version
列的注意事项
它可以认为是有用的BlogStoryVersion.CreatedDateTime
作为保持代表一个特定的“过去”值的列版本A的BlogStory。我认为这比 aVersionId
或更有益VersionCode
,因为从人们往往更熟悉时间概念的意义上说,它对用户更友好。例如,博客作者或读者可以以类似于以下的方式引用BlogStoryVersion:
1750
这是创建于26 August 2015
在9:30
”。该作者与编辑的角色:数据推导和解释
通过这种方法,可以很容易地分辨谁拥有“原始”的AuthorId
一个具体的BlogStory选择“最早的”版本有一定的BlogStoryId
自BlogStoryVersion
凭借应用的表MIN()函数来BlogStoryVersion.CreatedDateTime
。
这样,所有“以后”或“后继”版本行中BlogStoryVersion.AuthorId
包含的每个值自然表示手头相应版本的作者标识符,但也可以说这样的值同时表示相关用户作为博客故事“原始”版本的编辑者所扮演的角色。
是的,一个给定的AuthorId
值可能被多BlogStoryVersion
行共享,但这实际上是一条信息,它告诉了每个Version 的一些非常重要的东西,所以所述数据的重复不是问题。
DATETIME 列的格式
至于 DATETIME 数据类型,是的,您说得对,“ MySQL 以 ' YYYY-MM-DD HH:MM:SS
' 格式检索并显示 DATETIME 值”,但是您可以放心地以这种方式输入相关数据,并且当您必须执行查询时,您只需要利用内置的DATE 和 TIME 函数,除其他外,以适当的格式为您的用户显示相关值。或者您当然可以通过您的应用程序代码执行这种数据格式化。
BlogStory
UPDATE 操作的含义
每次BlogStory
一行发生 UPDATE 时,您必须确保在修改发生之前“存在”的相应值然后被插入到BlogStoryVersion
表中。因此,我强烈建议在单个ACID 事务中完成这些操作,以保证它们被视为不可分割的工作单元。您也可以使用触发器,但可以这么说,它们往往会使事情变得不整洁。
VersionId
或VersionCode
列如果您选择(由于业务情况或个人喜好)加入一个BlogStory.VersionId
或BlogStory.VersionCode
列来区分BlogStoryVersions,您应该考虑以下可能性:
AVersionCode
可能需要在 (i) 整个BlogStory
表和 (ii) 中都是唯一的BlogStoryVersion
。
因此,您必须实施经过仔细测试且完全可靠的方法,以便生成和分配每个Code
值。
也许,这些VersionCode
值可以在不同的BlogStory
行中重复,但绝不会与相同的BlogStoryNumber
. 例如,你可以有:
3
- 版本83o7c5c
,同时,86
- 版本83o7c5c
和958
- 版本83o7c5c
。后一种可能性开启了另一种选择:
保持一个VersionNumber
对BlogStories
,所以有可能是:
23
- 版本1, 2, 3…
;650
- 版本1, 2, 3…
;2254
- 版本1, 2, 3…
;尽管在同一个单独的基表中维护所有BlogStoryVersions是可能的,但我建议不要这样做,因为你会混合两种不同的(概念)类型的事实,从而对
但是,如果您选择遵循该行动方案,您仍然可以利用上面详述的许多想法,例如:
BlogStoryNumber
)和DATETIME柱(CreatedDateTime
);看到通过继续采用这种方法,一旦添加了“较新的”版本,就会复制一个BlogStoryNumber
值,您可以评估的选项(与上一节中提到的非常相似)正在建立一个PK列组成,并以这种方式,你将能够唯一标识每个版本A的BlogStory。你也可以尝试结合和。BlogStory
BlogStoryNumber
VersionCode
BlogStoryNumber
VersionNumber
您可能会找到我对这个帮助问题的回答,因为我还建议在相关数据库中启用时间功能来处理类似的场景。
归档时间: |
|
查看次数: |
12355 次 |
最近记录: |