为了补充查尔斯的答案,我将使用实体-属性-值模型,而不是为数据库中的每个其他表创建不同的历史表。
基本上,您会像这样创建一个 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
任何时候在您的一个表中创建或修改数据时创建一条记录。
按照您的示例,当您将“Kyle”添加到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,如果您不想为每个字段更改存储新记录,则可以将分组更改(例如在同一更新中更改为 'Kyle' 和30)存储为单个记录. 在这种情况下,您需要以 JSON 或其他一些 blob 格式表示更改的集合。这会将and字段合并为一个 ( )。例如:History
Name
Property
changedAttribute
newValue
changedValues
History
+==========+==========+================================+==============+
| tableId | recordId | changedValues | effectiveUtc |
| Employee | 1 | { Name: 'Kyle', Property: 30 } | N |
这可能比为数据库中的每个其他表创建历史表更困难,但它有多个好处:
- 向数据库中的表添加新字段不需要将相同的字段添加到另一个表
- 使用较少的表
- 随着时间的推移,更容易将更新关联到不同的表
这种设计的一个架构优势是您将应用程序的关注点与您的历史/审计功能解耦。这种设计与使用独立于应用程序数据库的关系甚至 NoSQL 数据库的微服务一样有效。