Domanda

Ho un db legacy con alcune tabelle in versione per il modo seguente: ogni campo individualmente - e solo quelli che sono cambiati;

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

La versione attuale è memorizzata in qualche altro tavolo: dire che ora sono 11; Quando si verifica la modifica, il vecchio valore dei dati viene registrato insieme al numero di versione "vecchio" (quello a cui appartiene il vecchio valore), quindi il numero di versione aumenta

Le tabelle hanno molti campi (> 20) e molti record, quindi immagino che l'idea iniziale fosse quella di utilizzare meno archiviazione per la versione. Ora devo aggiungere funzionalità per ricostruire i dati in un determinato momento (per versione). Come posso farlo in modo elegante ed efficiente, preferibilmente senza il SQL dinamico ma piuttosto con un approccio basato su set. Può essere fatto in SQL con buone prestazioni? Grazie!

È stato utile?

Soluzione

Puoi ricostruire i record. La query sarà un po 'ingombrante. La logica deve fare quanto segue per un determinato campo, il valore è dato dalle seguenti regole:

  1. Il nuovo valore del record con la versione più piccola successiva di @VersionNumber
  2. Il vecchio valore del record con la versione successiva successiva di @VersionNumber
  3. Il valore corrente

Ecco un esempio (con meno campi):

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;

Una sfida con questa logica è essere sicura che il valore corrente non si mescoli accidentalmente nei valori precedenti. Il left outer join insieme a seqnum = 1 gestisce questo. Il valore corrente viene utilizzato solo quando non c'è corrispondenza con un valore precedente o successivo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top