哪个选项对表的历史信息更好?

时间:2015-08-17 21:33:49

标签: sql database-design

我有一个表A,我存储了我的用户可以更新的信息,但由于用户的要求,我需要跟踪信息的变化。 考虑到:

  • 只要用户想要
  • ,就会显示此信息
  • 信息可以随时更改,但不要经常更改,让我们说一年5次

我选择了一些选项:

  • 将所有记录(旧记录和新记录)存储在一个表中
  • 创建2个表A和B,一个(A)仅保留当前记录,另一个(B)保持当前记录。在这种情况下,我会在B中插入新信息,然后我会对A进行更新。

我比第一种选择更喜欢第二种选择,但是我不确定第二种选择是否真的是解决方案,或者只是一种奇特的方式来做到这一点,因此我最终会将其存储起来数据量对吗?

有没有其他选择,或者您在开发时如何面对这种情况?

非常感谢!

AZU

3 个答案:

答案 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 2016Oracle 12c现在也支持时态表。另一种选择是查看您的DBMS是否提供了更改数据捕获选项,该选项可以配置为捕获每个更改并将其写入副本。 Oracle Golden GateSQL Server Change Data CaptureIBM Change Data Capture以这种方式工作。