我正在设计一个双时态数据库,我们在双时态表之间有1:N关系(我们也有M:N关系,但它们只是用连接器表和两个1:N关系建模,所以我考虑他们是1:N关系的特例。
为了说明这一点,让我们考虑一个包含两个表的简单案例:
|===============| |==================|
| tblOrder | | tblOrderItem |
|============== | |==================|
| - OrderId | | - OrderItemId |
| - OrderNumber | | - FK_OrderId |
|===============| | - Amount |
|==================|
FK_OrderId
是tblOrder
的外键。
为了使这个数据库模型具有双时态性,我提出了以下设计:
|===============| |==================| |====================|
| tblOrder | | tblOrderItem | | tblVersions |
|============== | |==================| |====================|
| - Id | | - Id | | - VersionId |
| - OrderId | | - OrderItemId | | - VersionDate |
| - OrderNumber | | - FK_OrderId | |====================|
| - VersionId | | - Amount |
| - IsDeleted | | - VersionId |
| - StartDate | | - IsDeleted |
| - EndDate | | - StartDate |
|===============| | - EndDate |
|==================|
VersionId
列是tblVersions
表的外键。对于数据库中的每个更改,都会创建tblVersions
表中的条目。那么数据的当前状态就是所有版本的总和。这样就可以重建数据库的先前状态(通过 WHERE VersionDate < ...
子句)。这是双重时间的交易时间维度。tblVersions table could also be avoided if we're just including the
VersionDate列列入两个数据表。StartDate
和EndDate
列是双重时间的有效时间维度。是的,EndDate
有点多余,我们可以使用StartTime
建模表格。Id
列是新的主键。由于同一实体有多行(多个版本,有效时间内的多个日期范围),因此实体的ID不能是表的主键。列OrderId
和OrderItemId
是实体的ID,但不再是表的主键。我们也可以将主键定义为Id
,而不是创建新的主键(OrderId, VersionId, StartDate)
。IsDeleted = 1
的条目。表中的所有其他条目(插入和更新)都有IsDeleted = 0
。FK_OrderId
的{{1}}列引用了tblOrderitem
的{{1}}列。这不再是真正的外键(在数据库约束的意义上),因为OrderId
不再是主键。但它仍然告诉我们哪个OrderItems是某个Order的一部分。这似乎运作良好,我们已经创建了必要的CRUD查询,并且能够读取和写入双时态数据。
我需要哪些约束才能始终如一地工作?
我对如何实现约束感兴趣(是否将它们实现为数据库约束,如tblOrder
或OrderId
约束,或FOREIGN KEY
s,或{{1}无论如何)。我只需要知道我需要的类型约束。
我想出了一些约束,我将作为答案发布。但也许还有更多?
答案 0 :(得分:0)
(我使用缩写 PIVT ='有效时间点'。这表示有效时间维度上的特定时间点)
以下是我已经想到的限制:
FK_VersionId
:显然,我们需要VersionId
列的标准外键约束。(OrderId, VersionId, StartDate)
必须是唯一的(tblOrderItem
也是如此)。StartDate
和EndDate
不重叠,StartDate
始终早于EndDate
。IsDeleted = 1
,如果有这样的行,则一定不能任何版本的实体在那一行之后。FK_OrderId
的值是否设置为标识存在于的Order实体的值给定的PIVT,已插入早期版本但尚未删除(通过设置IsDeleted = 1
)。