我有一个表A,我存储了我的用户可以更新的信息,但由于用户的要求,我需要跟踪信息的变化。 考虑到:
我选择了一些选项:
我比第一种选择更喜欢第二种选择,但是我不确定第二种选择是否真的是解决方案,或者只是一种奇特的方式来做到这一点,因此我最终会将其存储起来数据量对吗?
有没有其他选择,或者您在开发时如何面对这种情况?
非常感谢!
AZU
答案 0 :(得分:2)
我的建议是将表格转换为版本标准格式(vnf)。您说此表包含用户可能更新的数据。我推断,有一个独立的主表由静态数据组成,可更新表的PK也是独立表的FK。
create table Versions(
ID int not null,
ModDate date not null,
ModUserID int not null,
... ..., -- data fields
constraint PK_Versions primary key( ID, ModDate ),
constraint FK_Versions_Primary foreign key( ID )
references Primary( ID ),
constraint FK_Versions_User foreign key( ModUserID )
references Users( ID )
);
版本控制不需要将FK引用返回到主要版本,我只是将其包含在内以供说明。但它确实显示了为什么我称它为#34;版本正常形式。"您将从静态数据中规范化可变数据。这样,标准规范化技术就适用了。
大多数查询可能只对"当前"每个实体的版本。当前版本是最新版本 - 具有最大修改日期的版本。
select *
from Versions v
where v.ModDate =(
select Max( v1.ModDate )
from Versions v1
where v1.ID = v.ID );
不要让子查询让你担心。我已经使用版本多年了,查询速度非常快。
如果有主表,则显示整个当前元组的连接基于上面的查询。
select p.*, v.* -- You will want to expand these out
from Primary p
join Versions v
on v.ID = p.ID
and v.ModDate =(
select Max( v1.ModDate )
from Versions v1
where v1.ID = v.ID );
实际上,如果您查看第一个查询,则第二个查询可以只加入该视图。也不用担心加入简单的观点。如果检查完整查询的执行计划和视图的连接,它们应该是相同的。
您还可以从第二个查询创建一个视图,仅显示整个实体的当前版本。如果存在大量数据 - 许多实体的许多版本 - select * from view
将明显慢于仅包含当前行的表的类似转储。但是,如果您过滤数据 - select * from view where ID = 12345
- 结果应该类似。
但是这里设计的力量变得清晰了。假设您想知道过去某个特定点的实体版本。查询没有显着差异。考虑第一个查询:
select *
from Versions v
where v.ModDate =(
select Max( v1.ModDate )
from Versions v1
where v1.ID = v.ID
and v1.ModDate <= :DateOfInterest );
只需在子查询中添加and v1.ModDate <= :DateOfInterest
,您就可以及时回顾,看看数据在任何特定日期和时间的样子。
我的典型实现是拥有一个&#34; current&#34;视图,仅显示每个实体的当前版本和&#34;历史记录&#34;显示所有版本的视图。所有DML都通过&#34;当前&#34;视图。一个&#34;而不是&#34;触发器将每个操作转换为维护版本化数据所需的实际操作。例如,UPDATE将成为新版本的INSERT,当然,它将成为该实体的新当前版本。
答案 1 :(得分:0)
我通常会选择后者,除非我有一个可用的分区方法,可以将所有当前记录保存在他们自己的分区集中。
原因是否则您最终会将当前和旧记录混合在同一数据块中,这会降低缓存效率,因为通常不需要经常使用旧记录。
如果您使用前一种方法,我会标记“当前”版本的记录,以便通过索引轻松找到它们。
但是,这也会影响查询优化器对预期基数的估计。如果它看到20%的行是“当前”并且5%的行在上个月有一些日期(last_transaction?),则可能推断出1%的当前行在上个月有一个日期,而实际数字可能高于20%。
总的来说,如果历史记录很少被要求作为正常申请活动的一部分,我会把它们推到专用的桌子上。
答案 2 :(得分:0)
您绝对不希望使用当前指示符标记,因为这会极大地影响语义和对行所代表的内容的理解。每一行应代表世界上不同的实体。添加当前指标时,现在每行可能代表单个实体,或者它可能代表该实体在一段时间内的特征的版本。现在查询此表变得更加复杂且容易出错。
但在设计历史记录表之前,请检查您的DBMS是否提供任何时间特征。例如,DB2支持创建可以自动跟踪历史记录的时态表。 SQL Server 2016和Oracle 12c现在也支持时态表。另一种选择是查看您的DBMS是否提供了更改数据捕获选项,该选项可以配置为捕获每个更改并将其写入副本。 Oracle Golden Gate,SQL Server Change Data Capture和IBM Change Data Capture以这种方式工作。