如何有效地重建数据 - 由各个字段进行版本控制,只有那些已更改

时间:2013-11-07 21:10:00

标签: sql sql-server-2008 database-design versioning common-table-expression

我有一个遗留数据库,其中一些表通过以下方式进行版本化:每个字段单独 - 只有那些改变了;

Table1

ObjID  userID  Data1    Data2  Data3
-----  ------  -----    ----   ---- 
11       1       A      null   123  
222      1       H      111    999
33       2       C      222    333


Table1_ver

ObjID   userID   FieldName    OldValue    VersionNumber
-----  ------     -----        ----        ----
222       1       Data1         F           5
222       1       Data1         A           8
222       1       Data2        888          10
33        8       Data1         G           10

当前版本存储在其他一些表格中 - 现在说是11;当更改发生时,旧数据值与“旧”版本号(旧值所属的版本号)一起记录,然后版本号增加

这些表有很多字段(> 20)和大量记录,所以我想最初的想法是使用较少的存储空间进行版本控制。现在我需要添加功能来在给定的时间点(按版本)重建数据。我怎样才能以优雅高效的方式实现它 - 最好不使用动态SQL,而是采用一些基于集合的方法。可以在SQL中以良好的性能完成吗?谢谢!

1 个答案:

答案 0 :(得分:3)

您可以重建记录。查询会有点麻烦。逻辑是执行以下操作对于给定字段,该值由以下规则给出:

  1. 下一个小于@VersionNumber
  2. 的记录的新值
  3. 具有比@VersionNumber
  4. 更高版本的记录的旧值
  5. 当前值
  6. 以下是一个示例(字段较少):

    select t1.objId, t1.userId,
           max(case when tv.FieldName = 'Data1' and VersionNumber < @VersionNumber
                    then tv.NewValue
                    when tv.FieldName = 'Data1' and VersionNumber > @VersionNumber
                    then tv.OldValue
                    when tv.FieldName = 'Data1' and VersionNumber is null
                    then t.Data1
               end) as Data1,
           max(case when tv.FieldName = 'Data2' and VersionNumber < @VersionNumber
                    then tv.NewValue
                    when tv.FieldName = 'Data2' and VersionNumber > @VersionNumber
                    then tv.OldValue
                    when tv.FieldName = 'Data2' and VersionNumber is null
                    then t.Data2
               end) as Data2,
          . . . 
    from table1 t1 left outer join
         (select tv.*,
                 row_number() over (partition by objId, userId, fieldname
                                    order by abs(VersionNumber - @VersionNumber)
                                   ) as seqnum
          from table_var tv
         ) tv
         on tv.objId = t.objId and tv.userId = t.userId and seqnum = 1
    group by t1.objId, t1.userId;
    

    使用此逻辑的一个挑战是确保当前值不会意外混合以前的值。 left outer joinseqnum = 1处理此问题。仅当与前一个值或后一个值不匹配时,才会使用当前值。