Pergunta

Eu tenho um banco de dados herdado com algumas tabelas sendo versadas da seguinte maneira: cada campo individualmente - e apenas aqueles que mudaram;

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

A versão atual é armazenada em outra tabela - digamos que sejam 11 agora; Quando a alteração acontece, o valor antigo dos dados está sendo registrado junto com o número da versão "antigo" (aquele ao qual o valor antigo pertencia), então o número da versão aumenta

As tabelas têm muitos campos (> 20) e muitos registros, então acho que a idéia inicial era usar menos armazenamento para versões. Agora preciso adicionar funcionalidade para reconstruir os dados em um determinado momento (por versão). Como posso fazer isso de uma maneira elegante e eficiente - de preferência sem o SQL dinâmico, mas com alguma abordagem baseada em conjuntos. Pode ser feito no SQL com bom desempenho? Obrigado!

Foi útil?

Solução

Você pode reconstruir os registros. A consulta será um pouco pesada. A lógica é fazer o seguinte para um determinado campo, o valor é dado pelas seguintes regras:

  1. O novo valor do registro com a próxima versão menor que @versionNumber
  2. O valor antigo do registro com a próxima versão superior do que @versionNumber
  3. O valor atual

Aqui está um exemplo (com menos campos):

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;

Um desafio com essa lógica é ter certeza de que o valor atual não se mistura acidentalmente nos valores anteriores. o left outer join com seqnum = 1 lida com isso. O valor atual é usado apenas quando não há correspondência com um valor anterior ou seguinte.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top