2016-08-01 96 views
1

Ich ging durch post für die Versionierung von Tabellen auf Datensatzebene. Ich habe bemerkt, dass die Architektur die Verwendung von History-Tabellen behandelt. Mein Szenario erfordert jedoch kein Rollback, sondern das Abrufen von Zeitpunktzahlen. Hier habe ich mit einem Entwurf versucht, eine einzige Tabelle für die Versionierung zu verwenden. Beachten Sie, dass es sich um reine Basket-Tabellendaten handelt (keine Einschränkungen, Indizes usw.). Ich beabsichtige, basierend auf ID zu indizieren, da dies eine Gruppierung nach Klausel in der Spalte beinhaltet.Datenbankversionierung ohne Protokolltabellen

Zum Beispiel habe ich eine Tabelle Test bekam, wo

id die Kennung,

modstamp ist der Zeitstempel der Daten (nie null)

Neben der Spalten darüber, die Tabelle enthält Buchhaltungsspalten

local_modstamp ist die t imestamp, bei der die Aufzeichnung, bei der

del_modstamp ist der Zeitstempel aktualisiert wurde der Datensatz

Während der Sicherung werden alle Datensätze erhalten werden von der Quelle und eingefügt, wo die Datensätze, die Werte würden local_modstamp = gelöscht null und del_stamp = null.

id |modstamp     |local_modstamp |del_modstamp | 
---|---------------------------|---------------|-------------| 
1 |2016-08-01 15:35:32 +00:00 |    |    | 
2 |2016-07-29 13:39:45 +00:00 |    |    | 
3 |2016-07-21 10:15:09 +00:00 |    |    | 

Sobald die Datensätze erhalten werden, das sind die Szenarien für die Datenverarbeitung (vorausgesetzt, die Referenzzeit [ref_time] wird die Zeit, zu der der Prozess ausgeführt wird):

  1. Insert wie normal.

  2. Aktualisierung: Aktualisiere den letzten Datensatz mit local_modstamp = ref_time. Dann fügen Sie den neuen Datensatz ein. Die Abfrage wäre: Update Testset local_modstamp = wobei id = und local_modstamp nicht null ist und del_modstamp ist nicht null Einsatz in Testwerte (...)

  3. Löschen: mit del_modstamp den letzten Datensatz aktualisieren, = ref_time. Update Testset del_modstamp = wobei id = und local_modstamp ist nicht null und del_modstamp ist nicht null

Der Entwurf zielt darauf ab, die neuesten Aufzeichnungen bekommen, wo local_modstamp nicht null ist und del_modstamp ist nicht null. Allerdings lief ich in ein Problem, wo ich beabsichtige, die Abfrage (am weitesten innen Abfrage) abrufen Zeitpunkt mit:

select id, max(modstamp) from test where modstamp <= <ref_time> and (del_modstamp is null || del_modstamp <= <ref_time>) group by id; 

Es scheint, dass ich einen Fehler gemacht habe (habe ich?) Die Verwendung von null als ein Platzhalter, um die neuesten Datensätze der Tabelle zu identifizieren. Gibt es eine Möglichkeit, das vorhandene Design zu verwenden, um die Zeitpunkte zu erhalten?

Wenn nicht, denke ich, dass die wahrscheinliche Lösung darin besteht, den local_modstamp auf die neuesten Datensätze zu setzen. Dies würde erfordern, die Logik mit max (local_modstamp) im Falle von Updates zu aktualisieren. Kann ich auf meiner bestehenden Architektur bestehen bleiben, um die Zeitpunktzahlen zu erhalten?

Ich benutze SQL-Server jetzt, aber dieses Design kann auch auf andere Datenbankprodukte erweitert werden. Ich beabsichtige, einen allgemeineren Ansatz zu verwenden, um die Daten abzurufen, anstatt herstellerspezifische hacks zu verwenden.

Antwort

1

Einführung der Version Normal Form. Betrachten Sie diese Tabelle:

create table Entities(
    ID  int identity primary key, 
    S1  [type], -- Static data 
    Sn  [type], -- more static data 
    V1  [type], -- Volatile data 
    Vn  [type] -- more volatile data 
); 

Statische Daten sind Daten, die während der Lebensdauer des Unternehmens ändert sich nicht, oder das nicht-Tracking erforderlich. Flüchtige Datenänderungen und diese Änderungen müssen verfolgt werden.

Verschieben flüchtige Attribute zu einer separaten Tabelle:

create table EntityVersions(
    ID  int not null, 
    Effective date not null default sysdate(), 
    Deleted bit not null default 0, 
    V1  [type], 
    Vn  [type], 
    constraint PK_EntityVersions primary key(ID, Effective), 
    constraint FK_EntityVersionEntity foreign key(ID) 
     references Entities(ID) 
); 

Die Entities Tabelle enthält nicht mehr die flüchtigen Attribute.

Ein Einfügevorgang erstellt den Master-Entitätsdatensatz mit statischen Daten und generiert den eindeutigen ID-Wert. Dieser Wert wird verwendet, um die erste Version mit den Anfangswerten der flüchtigen Daten einzufügen. Ein Update führt im Allgemeinen nichts zur Master-Tabelle durch (es sei denn, ein statischer Wert wird tatsächlich geändert) und eine neue Version der neuen flüchtigen Daten wird in die Versionstabelle geschrieben. Beachten Sie, dass keine Änderungen an bestehenden Versionen vorgenommen werden, insbesondere nicht an der neuesten oder "aktuellen" Version. Die neue Version wird eingefügt, Ende der Operation.

Um die letzte Version oder jede Version tatsächlich rückgängig zu machen, löschen Sie diese Version einfach aus der Versionstabelle.

Zum Beispiel kann eine Tabelle Personal mit den folgenden Attributen:

EmployeeNum, HireDate, FirstName, LastName, PayRate, Dept, PhoneExt 

EmployeeNum wird natürlich zusammen mit HireDate Vornahme statisch sein. PhoneExt kann sich von Zeit zu Zeit ändern, aber das ist uns egal. Es wird also als statisch bezeichnet. Das endgültige Design ist:

Employees_S 
=========== 
    EmployeeNum (PK), HireDate, FirstName, PhoneExt 

Employees_V 
=========== 
    EmployeeNum (PK), Effective (PK), IsDeleted, LastName, PayRate, Dept 

Am 1. Januar 2016 stellten wir Sally Smith ein. Die statischen Daten werden in Employees_S eingefügt und generieren einen EmployeeNum-Wert von 1001. Mit diesem Wert fügen Sie auch die erste Version ein.

Employees_S 
=========== 
    1001, 2016-01-01, Sally, 12345 

Employees_V 
=========== 
    1001, 2016-01-01, 0, Smith, 35.00, Eng 

Am 1. März, sie eine Gehaltserhöhung bekommt:

Employees_S 
=========== 
    1001, 2016-01-01, Sally, 12345 

Employees_V 
=========== 
    1001, 2016-01-01, 0, Smith, 35.00, Eng 
    1001, 2016-03-01, 0, Smith, 40.00, Eng 

Am 1. Mai wird sie verheiratet:

Employees_S 
=========== 
    1001, 2016-01-01, Sally, 12345 

Employees_V 
=========== 
    1001, 2016-01-01, 0, Smith, 35.00, Eng 
    1001, 2016-03-01, 0, Smith, 40.00, Eng 
    1001, 2016-05-01, 0, Jones, 40.00, Eng 

Beachten Sie, dass Versionen der gleichen Einheit, andere als die Einschränkung, dass die Gültigkeitsdaten nicht identisch sein können, sind völlig unabhängig voneinander.

Um zu sehen, was der aktuelle Stand der Mitarbeiter 1001 aussieht, hier ist die Abfrage:

select s.EmployeeNum, s.HireDate, s.FirstName, v.LastName, v.PayRate, v.Dept, s.PhoneExt 
from Employees_S s 
join Employees_V v 
    on v.EmployeeNum = s.EmployeeNum 
    and v.Effective = (select Max(Effective) 
         from Employees_V 
         where EmployeeNum = v.EmployeeNum 
          and Effective <= SysDate()) 
where s.EmployeeNum = 1001 
    and v.IsDeleted = 0; 

Hier ist der interessante Teil. Um zu sehen, was der Zustand der Mitarbeiter 1001 sah aus wie auf, sagt Feb 11, hier ist die Abfrage:

select s.EmployeeNum, s.HireDate, s.FirstName, v.LastName, v.PayRate, v.Dept, s.PhoneExt 
from Employees_S s 
join Employees_V v 
    on v.EmployeeNum = s.EmployeeNum 
    and v.Effective = (select Max(Effective) 
         from Employees_V 
         where EmployeeNum = v.EmployeeNum 
          and Effective <= '2016-02-11') 
where s.EmployeeNum = 1001 
    and v.IsDeleted = 0; 

Es ist die gleiche Abfrage - mit Ausnahme der letzten Zeile der Unterabfrage. Aktuelle und historische Daten befinden sich in derselben Tabelle und werden mit derselben Anweisung abgefragt.

Hier ist ein weiteres cooles Feature. Es ist 1 Jul und wir wissen, dass Sally am 1. September mit einer weiteren Gehaltserhöhung in die Marketingabteilung wechselt. Der Papierkram ist bereits durchgegangen. Gehen Sie weiter und legen Sie die neuen Daten:

Employees_S 
=========== 
    1001, 2016-01-01, Sally, 12345 

Employees_V 
=========== 
    1001, 2016-01-01, 0, Smith, 35.00, Eng 
    1001, 2016-03-01, 0, Smith, 40.00, Eng 
    1001, 2016-05-01, 0, Jones, 40.00, Eng 
    1001, 2016-09-01, 0, Jones, 50.00, Mkt 

Die nächste zu der letzten Version noch als aktuelle Version angezeigt wird, aber die erste Abfrage ausgeführt am oder nach dem 1. September die Marketing-Daten zeigen.

Here sind die Folien einer Präsentation, die ich ein paar Mal auf Tech-Messen gemacht habe. Es enthält weitere Details darüber, wie all dies einschließlich der Abfragen durchgeführt werden kann. Und here ist ein Dokument, das viel detaillierter geht.

+0

Wenn ich eine Löschung durchführe, wird die neueste Version mit 1 markiert oder wird sie eingefügt und dann mit 1 gelöscht? Dies betrifft die Abfrage 'und v.IsDeleted = 0;' in der Abfrage. Zum Beispiel, Sally verlässt das Unternehmen am 2016-10-01, aber ich muss abfragen, was ist der Staat am 2016-06-01, wird ignoriert werdenDeleted in der Klausel den Zweck zu erreichen? Ich bin in einem Rätsel, ob ich die Tische normalisieren soll. Ich speichere ein paar Tausend in einem Schema, und ob sie zu zweien normalisiert werden, wird den Tisch nicht aufblähen. Kann dies portiert werden, um auf dem gleichen Tisch zu erfolgen? – dmachop

+0

Wenn keine statischen Daten vorhanden sind und jede Spalte in der Versionstabelle versioniert ist, kann die obige Lösung mit einer einzelnen Tabelle durchgeführt werden. Wann immer Sie den Zeitpunkt oder die neuesten Daten auswählen, müssen Sie eine temporäre Tabelle erstellen (wählen Sie eine eindeutige ID von der Version aus) und führen Sie dann die Verknüpfung nach Bedarf durch. – dmachop

+0

Die PK ist statisch und die Erfahrung zeigt, dass es selten andere statische Attribute gibt. Sie benötigen jedoch immer noch zwei Tabellen, selbst wenn die Haupttabelle nur die PK enthält. Sie benötigen eine nicht versionierte Master-Tabelle als Ziel von FKs aus anderen Tabellen. Wenn Sie die verschiedenen Versionierungsschemata durchlesen, war das große, unüberwindbare Problem immer der Mangel an referenzieller Integrität. Dies (vnf) löst dieses Problem. – TommCatt