In manchen Fällen ist es nötig, alle Änderungen einer (oder mehrerer) Tabelle(n) zu speichern, um diese später zurückverfolgen und wiederherstellen zu können.
Leider bietet MySQL hierfür keine implementierte Lösung. Aus diesem Grund ist es nötig, Anwendungen selbst so zu entwickeln, dass vorherige Versionen in der Datenbank beibehalten werden.
Aufbau der Tabellen
Um in einer Tabelle die Änderungen nachzuverfolgen, werden einige Spalten hinzugefügt. Der Datentyp dieser ist beispielhaft und kann anhand der Anwendung variieren.
change_id | INT | Fortlaufende Änderungsnummer dieses Datensatzes |
valid_from | DATETIME | Zeit, ab der der Datenbankrecord gültig ist |
valid_to | DATETIME | Zeit, bis zu der der Record gültig war |
Der Primärschlüssel der Tabelle wird um das Feld change_id erweitert und das Attribut auto_increment entfernt. Von nun an kann eine Objekt-ID mehrfach vorkommen, wenn die change_id unterschiedlich ist.
Die change_id gibt die Nummer der Änderung dieses Datensatzes an. Sie wird bei jeder Erzeugung oder Speicherung eines Objekts auf den vorherigen Wert plus Eins erhöht.
Das Feld valid_from wird beim Einfügen der Zeile in die Tabelle gesetzt. Es entspricht normalerweise dem aktuellen Timestamp, der mit der Funktion NOW() berechnet werden kann. Die Spalte valid_to wird dagegen standardmäßig auf NULL gesetzt. Bei veralteten oder gelöschten Datensätzen ist valid_to auf die Zeit gesetzt, bis zu der der Datensatz gültig war.
CRUD-Operationen
Auslesen (Read)
Um einen (oder mehrere) Datensätze aus einer Insert-Only-Tabelle auszulesen, wird die Abfragebedingung um die Bedingung ergänzt, dass valid_to NULL sein muss. Ist dies nicht der Fall, wäre der Datensatz nicht mehr gültig.
Einfügen (Create)
Das Einfügen geschieht wie auch bei normalen Tabellen mit der Ausnahme, dass das Feld valid_from auf die aktuelle Zeit gesetzt werden muss. Das Feld change_id wird entweder auf 0 oder 1 und das Feld valid_to auf NULL gesetzt.
Bearbeiten (Update)
Um ein Objekt zu bearbeiten, wird das Feld valid_to des bisher gültigen Datensatzes auf NULL gesetzt und ein neuer Record mit den aktualisierten Daten eingesetzt. valid_from der neuen Zeile sollte valid_to der vorherigen entsprechen und die change_id wird um den Wert 1 erhöht.
Löschen (Delete)
Das Löschen geschieht durch das Setzen des valid_to-Wertes des bisher gültigen Datensatzes.
Verwendung der Historie
Werden alle Daten wie angegeben behandelt, lässt sich die gesamte Historie rekonstruieren. Die Daten eines bestimmten Zeitpunkts lassen sich abrufen, indem man prüft, ob valid_from vor dem gegebenen Zeitpunkt lag oder diesem entspricht und valid_to gleich NULL ist oder nach dem gegeben Zeitpunkt lag.
Anwendung
Das gezeigte Schema lässt sich in dieser Art in der Praxis einsetzen. Wichtig ist dabei allerdings, dass sichergestellt ist, dass es zu jedem Zeitpunkt nur ein gültiges Exemplar von jedem Objekt gibt. Dies erhöht den Abfrage- und Schreibaufwand in der Datenbank.
Außerdem ist es natürlich möglich, die Spalten nach eigenem Ermessen anzupassen und gegebenenfalls zu ergänzen oder zu verringern.