我希望设计一个数据库来跟踪每一组变化,以便我将来可以参考它们。例如:
Database A
+==========+========+==========+
| ID | Name | Property |
1 Kyle 30
如果我将行的'property'字段更改为50,则应将该行更新为:
1 Kyle 50
但是应该保存行的属性在某个时间点为30的事实。然后,如果该行再次更新为70:
1 Kyle 70
应该保留行的属性为50和70的两个事实,这样我可以检索一些查询:
1 Kyle 30
1 Kyle 50
它应该认识到这些是在不同时间点的“相同条目”。
编辑:此历史记录需要在某个时间点呈现给用户,因此理想情况下,应该了解哪些行属于同一“修订群集”
处理此数据库设计的最佳方法是什么?
答案 0 :(得分:13)
一种方法是为数据库中的每个表创建一个MyTableNameHistory
,并使其模式与表MyTableName
的模式相同,只是History表的主键有一个附加列将effectiveUtc
命名为DateTime。例如,如果您有一个名为Employee
的表,
Create Table Employee
{
employeeId integer Primary Key Not Null,
firstName varChar(20) null,
lastName varChar(30) Not null,
HireDate smallDateTime null,
DepartmentId integer null
}
然后历史记录表将是
Create Table EmployeeHistory
{
employeeId integer Not Null,
effectiveUtc DateTime Not Null,
firstName varChar(20) null,
lastName varChar(30) Not null,
HireDate smallDateTime null,
DepartmentId integer null,
Primary Key (employeeId , effectiveUtc)
}
然后,您可以在Employee表上放置一个触发器,这样每次在Employee表中插入,更新或删除任何内容时,都会在EmployeeHistory表中插入一条新记录,其中所有常规字段的值都完全相同,以及effectiveUtc列中的当前UTC日期时间。
然后,要在过去的任何时间点找到值,只需从历史记录表中选择记录,其中effectiveUtc值是您想要该值的asOf日期时间之前的最高值。
Select * from EmployeeHistory h
Where EmployeeId = @EmployeeId
And effectiveUtc =
(Select Max(effectiveUtc)
From EmployeeHistory
Where EmployeeId = h.EmployeeId
And effcetiveUtc < @AsOfUtcDate)
答案 1 :(得分:3)
要添加到Charles' answer,我会使用Entity-Attribute-Value model,而不是为数据库中的每个其他表创建不同的历史记录表。
基本上,你会像这样创建 一个 History
表:
Create Table History
{
tableId varChar(64) Not Null,
recordId varChar(64) Not Null,
changedAttribute varChar(64) Not Null,
newValue varChar(64) Not Null,
effectiveUtc DateTime Not Null,
Primary Key (tableId , recordId , changedAttribute, effectiveUtc)
}
然后,只要您在其中一个表中创建或修改数据,就可以创建History
条记录。
按照你的例子,当你添加凯尔&#39;在Employee
表中,您将创建两条记录(每个非id属性一条),然后每次属性更改时都会创建一条新记录:
History
+==========+==========+==================+==========+==============+
| tableId | recordId | changedAttribute | newValue | effectiveUtc |
| Employee | 1 | Name | Kyle | N |
| Employee | 1 | Property | 30 | N |
| Employee | 1 | Property | 50 | N+1 |
| Employee | 1 | Property | 70 | N+2 |
或者,正如a_horse_with_no_name建议的那样,如果您不想为每个字段更改存储新的History
记录,则可以存储分组的更改(例如更改Name
将Kyle&#39;和Property
添加到同一更新中的30作为单个记录。在这种情况下,您需要以JSON或其他一些blob格式表达更改集合。这会将changedAttribute
和newValue
字段合并为一个(changedValues
)。例如:
History
+==========+==========+================================+==============+
| tableId | recordId | changedValues | effectiveUtc |
| Employee | 1 | { Name: 'Kyle', Property: 30 } | N |
这可能比为数据库中的每个其他表创建一个History表更困难,但它有多个好处:
此设计的一个体系结构优势是您可以解决应用程序和历史/审计功能的问题。这种设计与使用与应用程序数据库分开的关系甚至NoSQL数据库的微服务一样有效。
答案 2 :(得分:1)
最好的方法取决于你在做什么。您希望更深入地了解缓慢变化的维度:
https://en.wikipedia.org/wiki/Slowly_changing_dimension
在Postgres 9.2中也不要错过tsrange类型。它允许将start_date
和end_date
合并到一个列中,并使用GIST(或GIN)索引和排除约束对内容进行索引,以避免重叠日期范围。
编辑:
应该了解哪些行属于同一个“修订群集”
在这种情况下,您想要表格中的日期范围,而不是版本号或实时标记,否则您最终会在整个地方复制相关数据。
另请注意,请考虑将审核表与实时数据区分开来,而不是将所有内容存储在同一个表中。实施和管理起来比较困难,但它可以对实时数据进行更有效的查询。
也请参阅此相关帖子:Temporal database design, with a twist (live vs draft rows)
答案 3 :(得分:1)
记录所有更改的方法之一是创建所谓的audit triggers
。此类触发器可以将对它们所在表的任何更改记录到单独的日志表中(可以查询该表以查看更改的历史记录)。
有关实施的详情here
。