Как эффективно реконструировать данные - версировать отдельными полями, только те, которые изменились

StackOverflow https://stackoverflow.com/questions/19846759

Вопрос

У меня есть Legacy DB с некоторыми таблицами, которые версируют следующим образом: каждое поле индивидуально - и только те, которые изменились;

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. Новое значение записи со следующей меньшей версией, чем @versionnumber
  2. Старое значение записи со следующей более высокой версией, чем @versionnumber
  3. Текущее значение

Вот пример (с меньшим количеством полей):

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 join с seqnum = 1 обрабатывает это. Текущее значение используется только тогда, когда нет совпадения с предыдущим или последующим значением.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top