我希望有一个数据库表来保存包含修订历史记录的数据(比如Wikipedia上的页面)。我认为一个好主意是有两列标识行:(name, version)
。所以样本表看起来像这样:
TABLE PERSONS:
id: int,
name: varchar(30),
version: int,
... // some data assigned to that person.
因此,如果用户想要更新人员的数据,他们就不会进行更新 - 相反,他们会创建一个具有相同name
但不同version
值的新PERSONS行。向用户显示的数据(对于给定的name
)是具有最高version
的数据。
我有第二张表,比如说DOGS,它引用了PERSONS表中的人物:
TABLE DOGS:
id: int,
name: varchar(30),
owner_name: varchar(30),
...
显然,owner_name
是对PERSONS.name
的引用,但我不能将其声明为外键(在MS SQL Server中),因为PERSONS.name
不是唯一的!
问题:那么,在MS SQL Server 2008中,我应该如何确保数据库的完整性(即,对于每个DOG,PERSONS中至少存在一行,使得其PERSON.name = = DOG.owner_name)?
我正在寻找最优雅的解决方案 - 我知道我可以在PERSONS表上使用触发器,但这并不像我希望的那样具有声明性和优雅性。有什么想法吗?
其他信息
上面的设计具有以下优点:如果我需要,我可以“记住”一个人当前的id
(或(name, version)
对)并且我确信该行中的数据永远不会改变了。这很重要,例如如果我将此人的数据作为随后打印的文档的一部分,并且在5年内有人可能希望打印完全未更改的副本(例如,使用与今天相同的数据),那么这对他们来说非常容易
也许您可以想到一个完全不同的设计,它可以实现相同的目的,并且可以更容易地实施其完整性(最好使用外键或其他约束)?
编辑:感谢Michael Gattuso的回答,我发现了另一种可以描述这种关系的方式。有两种解决方案,我将其作为答案发布。请投票选出你喜欢哪一个。
答案 0 :(得分:5)
在父表中,在(id,version)上创建唯一约束。将版本列添加到子表,并使用检查约束以确保它始终为0.使用FK约束将(parentid,version)映射到父表。
答案 1 :(得分:2)
或者,您可以维护具有历史价值的数据的人员历史记录表。通过这种方式,您可以保持人员和狗的表格整洁,参考文献简单,但也可以访问历史上有趣的信息。
答案 2 :(得分:1)
好的,首先,您需要规范化表格。谷歌“数据库规范化”,你会得到大量的阅读。特别是PERSONS表需要注意。
第二件事是,当您创建外键引用时,99.999%的时间要引用ID(数字)值。即,[DOGS]。[所有者]应该是对[PERSONS]的引用。[id]。
编辑:添加示例模式(原谅松散的语法)。我假设每只狗只有一个主人。这是实现人员历史的一种方式。所有列都不为空。
Persons Table:
int Id
varchar(30) name
...
PersonHistory Table:
int Id
int PersonId (foreign key to Persons.Id)
int Version (auto-increment)
varchar(30) name
...
Dogs Table:
int Id
int OwnerId (foreign key to Persons.Id)
varchar(30) name
...
最新版本的数据将直接存储在Persons表中,旧数据存储在PersonHistory表中。
答案 3 :(得分:0)
我会使用和关联表将多个版本链接到一个pk。
答案 4 :(得分:0)
我参与过的一个项目解决了类似的问题。这是一个生物记录数据库,随着新研究提高对分类学的理解,物种名称可以随时间发生变化。
然而,旧记录需要与原始物种名称保持相关。它变得复杂,但基本的解决方案是有一个NAME表,它只包含所有独特的物种名称,一个代表实际物种的物种表和一个将两者联系在一起的NAME_VERSION表。在任何时候都会有一个首选名称(即物种当前接受的科学名称),这是name_version中保存的布尔字段。
在您的示例中,这将转换为Details表(detailsid,otherdetails列)名为DetailsVersion(detailsid,personid)的链接表和Person表(personid,非更改数据)。将狗与人联系。
答案 5 :(得分:0)
id(int),
名,
.....
activeVersion(这将是来自personVersionInfo的UID)
注意:上面的表每个人都有1行。将有创建人的原始信息。
UID(识别人+版本的唯一标识符),
id(int),
名,
.....
versionId(这将为每个人生成)
DogID,
DogName
......
UID,
DogID
编辑:你必须加入PersonWithDogs,PersionVersionInfo,Dogs才能全面了解(截至今天)。这种结构将帮助您将狗链接到所有者(具有特定版本)。
如果Person的信息发生变化并且您希望获得与Dog相关的最新信息,则必须更新PersonWithDogs表以获得给定Dog的所需UID(人员)。
您可以在PersonWithDogs中使用DogID等限制 在这种结构中,UID(人)可以有很多狗。
您的方案(可以更改/限制等)将有助于更好地设计架构。
答案 6 :(得分:0)
感谢Michael Gattuso的回答,我发现了另一种可以描述这种关系的方式。有两种解决方案,这是第一种。请投票选出你喜欢哪一个。
解决方案1
在PERSONS表中,我们只保留名称(唯一标识符)和当前人员数据的链接:
TABLE PERSONS:
name: varchar(30),
current_data_id: int
我们创建了一个新表PERSONS_DATA,其中包含该人的所有数据历史记录:
TABLE PERSONS_DATA:
id: int
version: int (auto-generated)
... // some data, like address, etc.
DOGS表保持不变,它仍然指向一个人的名字(FK到PERSONS表)。
优点:对于每只狗,至少存在一个包含其所有者数据的PERSONS_DATA行(这就是我想要的)
DISADVANTAGE:如果您想更改某人的数据,您必须:
答案 7 :(得分:0)
感谢Michael Gattuso的回答,我发现了另一种可以描述这种关系的方式。有两种解决方案,这是第二种解决方案。请投票选出你喜欢哪一个。
解决方案2
在PERSONS表中,我们只保留名称(唯一标识符)和第一个(非当前!)人员数据的链接:
TABLE PERSONS:
name: varchar(30),
first_data_id: int
我们创建了一个新表PERSONS_DATA,其中包含该人的所有数据历史记录:
TABLE PERSONS_DATA:
id: int
name: varchar(30)
version: int (auto-generated)
... // some data, like address, etc.
DOGS表保持不变,它仍然指向一个人的名字(FK到PERSONS表)。
优点:
DISADVANTAGE:要检索当前人的数据,我必须:
您怎么看?