Änderungshistorie implantieren

Die neuen Tabellenereignisse, die mit Access 2010 eingeführt wurden, erlauben die automatische Sicherung von Tabellendaten vor der Durchführung von änderungen an den Datensätzen. Dazu legen Sie entsprechende Datenmakros an, die durch die Tabellenereignisse ausgelöst werden. Außerdem benötigen Sie eine Tabelle, welche die geänderten Datensätze speichert. Wenn Sie beides für mehrere Tabellen durchführen wollen, ist dies eine Menge Handarbeit. Grund genug, diesen Vorgang zu automatisieren.

Dazu eignet sich natürlich ein Access-Add-In am besten: Dieses können Sie in allen betroffenen Datenbanken starten und die Tabellen mit den zu sichernden Daten präparieren. Dazu soll die Tabelle zunächst kopiert und unter einem passenden Namen gespeichert werden – bei der Tabelle tblArtikel beispielsweise unter dem Namen _tblArtikel_Backup. Diese Tabelle soll zusätzlich noch zwei Felder enthalten, die das änderungsdatum oder das Löschdatum aufnehmen.

Die Lösung setzt auf dem Beitrag Geänderte Daten archivieren auf (www.access-im-unternehmen.de/925).

Aussehen der Makros zum Erstellen des Backups

Das Makro für das Speichern der Daten der Tabelle etwa nach der Aktualisierung eines Datensatzes sieht wie in Bild 1 aus.

Aufbau des Makros zum Sichern der Daten vor dem Aktualisieren

Bild 1: Aufbau des Makros zum Sichern der Daten vor dem Aktualisieren

Der erste Teil prüft, ob die Option zum Deaktivieren der Sicherung gegebenenfalls deaktiviert ist. Dazu muss eine Tabelle namens tblOptionen vorhanden sein. Diese können Sie gegebenenfalls auch unter einem anderen Namen anlegen, da viele Anwendungen bereits über eine solche Tabelle verfügen.

Die folgenden Befehle erstellen einen neuen Datensatz in der jeweiligen Zieltabelle und stellen die Werte der Felder auf die Werte vor der änderung ein. Diese Werte liefert jeweils die Tabelle Alt.

Schließlich weist das Makro dem Feld GeaendertAm noch den änderungszeitpunkt zu.

Beim Löschen eines Datensatzes sieht es ähnlich aus – der einzige Unterschied besteht darin, dass der Löschzeitpunkt im Feld GeloeschtAm gesichert wird.

Einschränkungen

Für das Sichern von Daten per Tabellenereignis gibt es eine Einschränkung bezüglich der Felddatentypen: Von der Sicherung sind nämlich sowohl Memofelder als auch Anlagefelder und mehrwertige Felder ausgeschlossen. Die Backup-Tabellen müssen diese Felder also gar nicht erst aufnehmen und auch das Tabellenereignis berücksichtigt diese nicht.

Deutsch-Englisch

Ein weiteres Problem ist die automatische übersetzung des Makro-Codes. Wenn Access nämlich ein deutsches Literal erkennt, das in der englischen Version irgendeinem Access-Schlüsselwort entspricht, dann übersetzt Access dieses automatisch.

Sollten Sie also in einem Datenmakro den Feldnamen [Old].[Beschreibung] verwenden, wird dieser in [Old].[Description] geändert. Das ist unbefriedigend, aber außer durch Umgehung der entsprechenden Feldnamen wohl kaum zu lösen.

Aufgaben des geplanten Add-Ins

Das zu erstellende Add-In soll also folgende Aufgaben erledigen:

  • Erstellen einer Tabelle zum Speichern der Optionen, falls noch nicht vorhanden
  • Erstellen der Tabelle zum Sichern der Daten einer Tabelle
  • Bereitstellen des Tabellenereignisses und des Datenmakros zum Kopieren der Daten vor der änderung beziehungsweise vor dem Löschen des Datensatzes.

Das einzige Formular des Add-Ins dazu sieht wie in Bild 2 aus. Es bietet die folgenden Steuerelemente:

Add-In-Formular zum Anlegen von Sicherungstabellen und zum Generieren der benötigten Makros

Bild 2: Add-In-Formular zum Anlegen von Sicherungstabellen und zum Generieren der benötigten Makros

  • Das oberste Textfeld txtOptionentabelle gibt den Namen der Tabelle an, welche die Optionen für das Add-In speichert. Diese wird, wenn das Add-In erstmalig von einer Anwendung aus geöffnet wird, automatisch angelegt. Der Benutzer darf den Namen der Optionentabelle dabei beliebig anpassen.
  • Das zweite Textfeld txtSicherungstabelle legt das Schema für die Benennung der Archivtabellen fest. Einziges fixes Element ist der Platzhalter [Tabelle]. Dieser wird beim Zusammenstellen des Tabellennamens durch den Namen der Originaltabelle ersetzt.
  • Das Listenfeld lstTabellen zeigt alle Tabellen der aktuellen Datenbank an. Hier wählen Sie die Tabelle aus, für die eine Archivtabelle erstellt werden soll und für die das Add-In die Datenmakros zum automatischen Archivieren generieren soll.
  • Die Schaltfläche cmdProtokollierungEinrichten startet die wesentlichen Schritte, nämlich das Anlegen der Archivtabelle sowie die Generierung der beiden Makros.
  • Das Textfeld txtMakroAktualisierung zeigt das Makro an, das Sie dem Tabellenereignis Nach Aktualisierung hinzufügen.
  • Das Textfeld txtMakroLoeschung liefert das entsprechende Datenmakro für das Ereignis Nach Löschung.
  • Schließlich gibt es noch zwei Schaltflächen, mit denen Sie die XML-Dokumente für die Datenmakros in die Zwischenablage kopieren und dann für die Makros einsetzen können.

Datenmakros kopieren

Wenn Sie die Tabelle ausgewählt und die Schaltfläche Protokollierung einrichten angeklickt haben, erhalten Sie die beiden benötigten Datenmakros mit den XML-Dokumenten aus den beiden unteren Textfeldern.

Diese können Sie zur Ansicht oder für Notfälle nutzen, falls das automatische Hinzufügen der Datenmakros zu den Tabellen nicht gelingt.

Und hier ist der Notfallplan: Wenn Sie beispielsweise die Datensätze der Tabelle tblAnreden archivieren möchten, erhalten Sie mit der Konfiguration aus dem Screenshot eine neue Tabelle namens tblAnreden_Archiv.

Kopieren Sie dann den Inhalt des Textfeldes mit dem Makro für den Fall der Aktualisierung in die Zwischenablage. öffnen Sie dann die Tabelle, hier tblAnreden, beispielsweise in der Datenblattansicht.

Nun wählen Sie im Ribbon den Eintrag Tabelle|Nach-folge-er-eignisse|Nach Aktualisierung aus (s. Bild 3). Es erscheint ein leeres Makro, das Sie nun einfach markieren und dann per Strg + C den Inhalt der Zwischenablage einfügen.

Ribbon-Befehl zum Anzeigen des Datenmakros, das durch das Tabellenereignis Nach Aktualisierung ausgelöst wird

Bild 3: Ribbon-Befehl zum Anzeigen des Datenmakros, das durch das Tabellenereignis Nach Aktualisierung ausgelöst wird

Und siehe da: Der Generator hat tatsächlich einen XML-Code zusammengestellt, der die benötigten Makrobefehle liefert (s. Bild 4).

Frisch eingefügtes Datenmakro

Bild 4: Frisch eingefügtes Datenmakro

Auf die gleiche Weise gehen Sie nun noch für das Datenmakro Nach Löschung vor. Damit haben Sie der Tabelle zwei Makros hinzugefügt, die dafür sorgen, dass die letzte Version eines Datensatzes vor einer änderung oder Löschung in der jeweiligen Archivtabelle, hier tblAnreden_Archiv, gespeichert wird.

Aber, wie eingangs erwähnt: Normalerweise sollte das Add-In die Datenmakros automatisch hinzufügen.

Achtung: Ersetzen vorhandener Datenmakros

Beim Testen des Add-Ins ist aufgefallen, dass es beim überschreiben vorhandener Datenmakros zu Problemen kommen kann. Wir sind so vorgegangen, dass wir den kompletten vorhandenen Inhalt des Datenmakros mit Strg + A markiert und diesen dann durch Betätigen der Tastenkombination Strg + C durch den Inhalt der Zwischenablage ersetzt haben. Zumindest haben wir das gedacht! Der Inhalt wurde nämlich mitnichten ersetzt, sondern blieb einfach an Ort und Stelle. Also gehen Sie beim Ersetzen des Inhalts eines Datenmakros einfach wie folgt vor: Markieren Sie den kompletten Inhalt mit Strg + A, löschen Sie diesen und fügen dann den neuen Inhalt mit Strg + C aus der Zwischenablage ein.

Technischer Hintergrund des Add-Ins

In den folgenden Abschnitten schauen wir uns die technischen Hintergründe des Add-Ins an. Dabei arbeiten wir uns anhand des Formulars und des oben beschriebenen Ablaufs vor.

Den Start macht dabei das Ereignis Beim Laden, das die Ereignisprozedur aus Listing 1 auslöst. Sie ruft zunächst eine Funktion namens OptionentabelleFinden auf und übergibt dieser einen Rückgabeparameter namens strOptionentabelle, der modulweit wie folgt deklariert wird:

Private Sub Form_Load()
     If OptionentabelleFinden(strOptionentabelle) = True Then
         Me!txtOptionentabelle = strOptionentabelle
     Else
         strOptionentabelle = "tblOptionenArchiv"
         strOptionentabelle = InputBox("Keine Optionentabelle gefunden. " _
             & "Geben Sie den Namen der zu erstellenden  Tabelle ein.", _
             "Optionentabelle fehlt", strOptionentabelle)
         If Not Len(strOptionentabelle) = 0 Then
             If OptionentabelleAnlegen(strOptionentabelle) = True Then
                 Me!txtOptionentabelle = strOptionentabelle
             End If
         End If
     End If
     Me!lstTabellen.RowSourceType = "Value List"
     Me!lstTabellen.RowSource = TabellenEinlesen
     Me!txtSicherungstabelle = DLookup("NameDerArchivtabelle", strOptionentabelle)
End Sub

Listing 1: Diese Prozedur wird beim Laden des Formulars ausgelöst.

Dim strOptionentabelle As String

Die Funktion hat die Aufgabe, aus den Tabellen der Anwendung diejenige zu identifizieren, die vom Add-In als Optionentabelle definiert wurde. Diese wird nicht am Namen erkannt, sondern anhand einer speziell zu diesem Zweck zu der Tabelle hinzugefügten Eigenschaft namens Optionentabelle. Die Funktion OptionentabelleFinden schauen wir uns weiter unten an, zunächst reicht es uns zu wissen, dass diese Funktion die Variable strOptionentabelle mit dem Namen der Tabelle füllt – oder aber den Wert False als Funktionswert zurückliefert, wenn keine passende Tabelle vorhanden ist.

Liegt eine Optionentabelle vor, trägt die Prozedur ihren Namen in das Textfeld txtOptionentabelle ein, anderenfalls fragt sie vom Benutzer per InputBox den gewünschten Namen für die Optionentabelle ab. Damit ruft die Prozedur eine weitere Funktion namens OptionentabelleAnlegen auf und übergibt dieser den Namen der anzulegenden Tabelle. Diese Funktion (siehe weiter unten) legt die Tabelle an und liefert im Erfolgsfall den Wert True zurück.

Schließlich stellt die Prozedur die Eigenschaft Datensatzherkunft für das Listenfeld lstTabellen auf einen Wert ein, den die Funktion TabellenEinlesen liefert. Das Ergebnis dieser Funktion ist eine kommaseparierte Liste der Tabellennamen der aktuellen Anwendung. Schließlich liest die Prozedur noch den aktuellen Ausdruck für die Erstellung der Archivtabellen aus dem Feld NameDerArchivtabelle aus der Optionentabelle aus und trägt diesen in das Textfeld txtSicherungstabelle ein.

Optionstabelle erstellen

In der Regel müssen Sie einer Anwendung immer erst eine Optionentabelle hinzufügen, sofern diese noch nicht vorhanden ist. Dies erledigt die Funktion OptionentabelleAnlegen aus Listing 2. Diese erwartet den Namen der zu erstellenden Tabelle als Parameter. Die Funktion erstellt zunächst die Tabelle mit dem angegebenen Namen als neues TableDef-Objekt und fügt dieser dann das Feld ArchivierungDeaktiviert sowie das Feld NameDerArchivtabelle hinzu. Dies geschieht in zwei Schritten – dem eigentlichen Erstellen mit der Methode CreateField des TableDef-Objekts und dem Anhängen an die Fields-Auflistung der Tabelle. Danach hängt die Funktion die Tabelle selbst an die TableDefs-Auflistung an.

Private Function OptionentabelleAnlegen(strOptionentabelle As String) As Boolean
     Dim db As DAO.Database
     Dim tdf As DAO.TableDef
     Dim prp As DAO.Property
     Dim fld As DAO.Field
     Set db = CurrentDb
     Set tdf = db.CreateTableDef(strOptionentabelle)
     Set fld = tdf.CreateField("ArchivierungDeaktiviert", dbBoolean)
     tdf.Fields.Append fld
     Set fld = tdf.CreateField("NamederArchivtabelle", dbText, 255)
     tdf.Fields.Append fld
     db.TableDefs.Append tdf
     Set prp = tdf.CreateProperty("Optionentabelle", dbBoolean, True)
     tdf.Properties.Append prp
     db.TableDefs.Refresh
     Application.RefreshDatabaseWindow
     db.Execute "INSERT INTO " & strOptionentabelle _
         & "(ArchivierungDeaktiviert, NameDerArchivTabelle)  " _
         & "VALUES(0, ''[Tabelle]_Archiv'')", dbFailOnError
     OptionentabelleAnlegen = True
End Function

Listing 2: Anlegen der Optionentabelle

Damit das Add-In später auf irgendeine Weise die neu erstellte Optionentabelle identifizieren kann, fügen wir dieser noch eine benutzerdefinierte Eigenschaft hinzu. Dazu erstellt die Funktion mit der CreateProperty-Funktion eine neue Property namens Optionentabelle und stellt den Datentyp auf Boolean ein. Danach hängt sie die Property an die Properties-Auflistung an. Die Refresh-Methode aktualisiert die TableDefs-Auflistung und die Methode RefreshDatabaseWindow sorgt dafür, dass die neue Tabelle gleich im Navigationsbereich erscheint.

Schließlich fügt die Funktion direkt noch einen einzigen Datensatz zur Tabelle hinzu und stellt diesen auf den Wert False ein. Die Tabelle sieht nun wie in Bild 5 aus.

Tabelle zum Speichern einer Option

Bild 5: Tabelle zum Speichern einer Option

Optionentabelle auffinden

Natürlich müssen wir dem Add-In auch eine Funktion hinzufügen, mit der dieses beim Starten prüfen kann, ob die benötigte Optionentabelle bereits vorhanden ist. Dies erledigt die Funktion OptionentabelleFinden aus Listing 3. Diese erwartet ebenfalls einen Parameter namens strOptionentabelle, dieser soll jedoch mit dem Namen der gefundenen Tabelle als Rückgabewert gefüllt werden. Außerdem gibt der Funktionswert selbst einen Boolean-Wert zurück, der aussagt, ob eine Optionentabelle gefunden wurde.

Private Function OptionentabelleFinden(strOptionentabelle As String) As Boolean
     Dim db As DAO.Database
     Dim tdf As DAO.TableDef
     Dim strTemp As String
     Dim bolOptionentabelle As Boolean
     Set db = CurrentDb
     For Each tdf In db.TableDefs
         On Error Resume Next
         strTemp = tdf.Properties("Optionentabelle").Name
         bolOptionentabelle = Err.Number = 0 And Not (Left(tdf.Name, 1) = "~")
         On Error GoTo 0
         If bolOptionentabelle = True Then
             strOptionentabelle = tdf.Name
             OptionentabelleFinden = True
             Exit Function
         End If
     Next tdf
End Function

Listing 3: Auffinden einer bereits vorhandenen Optionentabelle

Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...

den kompletten Artikel im PDF-Format mit Beispieldatenbank

diesen und alle anderen Artikel mit dem Jahresabo

Schreibe einen Kommentar