Wiederherstellungspunkt für Daten

Während Sie eine Datenbank entwickeln, geben Sie Beispieldaten ein, testen die neu hinzugefügten Formulare und VBA-Prozeduren anhand dieser Daten, prüfen das Ergebnis und stellen die Beispieldaten gegebenenfalls wieder her, um weitere Tests mit geänderten Prozeduren durchzuführen. Bei komplizierteren Prozeduren kann dies einige Iterationen in Anspruch nehmen und das manuelle Bereitstellen der Testdaten wird zu einer hinderlichen und zeitraubenden Prozedur. Zu diesem Zweck stellen wir im vorliegenden Beitrag Techniken vor, mit denen Sie per Knopfdruck alle Daten aus definierten Tabellen sichern und diese mit einem weiteren Knopfdruck wiederherstellen.

Wenn Sie eine neue Datenbank entwickeln, stehen Sie ohnehin zunächst einmal vor dem Problem, dass Sie schnell Beispieldaten für ein oder mehrere Tabellen erstellen müssen. Für diesen Fall empfiehlt sich immer noch die Nutzung des Tools aus dem Beitrag Der Beispieldaten-Assistent (www.access-im-unternehmen.de/745).

Wenn Sie aber damit einmal brauchbare Beispieldaten entworfen haben, möchten Sie diesen Assistenten vermutlich nicht nach jeder änderung der Daten erneut heranziehen – zumal dieser immer nur eine Tabelle gleichzeitig füllen kann und dies bei mehreren Tabellen doch aufwendig wird.

Tests ändern Daten

Nun haben Sie aber vielleicht eine Datenbank mit einem Datenmodell entworfen, das mehrere verknüpfte Tabellen enthält, die Sie mehr oder weniger mühsam mit Testdaten gefüllt haben. Dann führen Sie ein paar Tests mit frisch erstellten Formularen oder neu programmierten Routinen durch und ändern die Daten dabei – Sie ändern Feldinhalte, fügen neue Datensätze hinzu oder löschen vorhandene Datensätze. Wenn die zu testenden Aktionen nicht das gewünschte Ergebnis bringen, möchten Sie diese möglichst mit den gleichen Testdaten erneut durchführen. Dazu müssen Sie aber die Testdaten wiederherstellen.

Programmlogik und Daten als Backup

Eine Methode wäre es, vor dem Durchführen ein Backup der Datenbank herzustellen und nach missglücktem Test mit dem vorherigen Stand weiterzuarbeiten. Keine schlechte Idee, aber dadurch, dass Sie immer komplett auf eine vorherige Version zurückgreifen, verlieren Sie möglicherweise auch änderungen, die Sie beim Testen etwa beim Debuggen des Codes durchgeführt haben.

Tabellenbackup per Backend

Die zweite Variante wäre es, die Daten komplett in ein Backend auszulagern und dieses vor dem ändern der Daten zu kopieren und anschließend wiederherzustellen. Damit würden Sie auf jeden Fall die änderungen der Programmlogik beibehalten, da Sie das Frontend nicht austauschen müssen.

Dies bedeutet allerdings auch, dass Sie die Datenbank gleich zu Beginn aufteilen müssen. Wenn allerdings auch das Datenmodell noch nicht komplett ausgereift ist, kommt weiterer Aufwand auf Sie zu:

Sie müssen beispielsweise die Tabellen nach jeder änderung des Datenmodells neu mit dem Frontend verknüpfen und müssen überdies aufpassen, dass Sie keine änderungen am Backend vornehmen und dieses dann durch eine ältere Version des Backends ersetzen.

Reines Datenbackup

Es gibt noch eine dritte Methode, auf die wir in diesem Beitrag detaillierter eingehen. Diese soll es erlauben, per Knopfdruck eine Kopie aller aktuell in der Datenbank verwendeten Tabellen zu erstellen und die in den Kopien enthaltenen Daten ebenfalls per Knopfdruck in die Tabellen zurückzuschreiben.

Das bedeutet, dass im ersten Schritt für jede Tabelle der Datenbank eine Kopie angelegt wird, deren Name beispielsweise aus dem Namen der Originaltabelle plus einem Zusatz besteht, sodass aus tblKunden beispielsweise tblKunden_Backup wird. Dieser Vorgang ist nicht besonders aufwendig: Mit einer SELECT INTO-Anweisung erstellen Sie schnell eine neue Tabelle auf Basis einer bestehenden Tabelle, welche genauso aufgebaut ist wie die Originaltabelle und auch gleich mit den identischen Daten gefüllt wird.

Im zweiten Schritt müssen dann nur die geänderten Inhalte der Originaltabellen gelöscht und durch die gespeicherten Inhalte ersetzt werden. Einfacher geht es doch nicht!

Leider gibt es einen kleinen Haken: Wenn die Tabellen miteinander verknüpft sind und für die Beziehungen referenzielle Integrität definiert ist, dann können Sie die Daten nicht in beliebiger Reihenfolge anlegen. Angenommen, die Tabelle tblKunden enthält ein Fremdschlüsselfeld namens AnredeID, das zur Auswahl der Datensätze der Tabelle tblAnreden dient, und es gibt eine Beziehung mit referenzieller Integrität zwischen dem Feld AnredeID der Tabelle tblKunden und dem gleichnamigen Feld der Tabelle tblAnreden. Dann können Sie zwar erst die Datensätze der Tabelle tblAnredeID hinzufügen und dann die Datensätze der Tabelle tblKunden, aber nicht umgekehrt: Beim Hinzufügen eines Datensatzes zur Tabelle tblKunden, dessen Feld AnredeID einen Wert enthält, der noch nicht in der Tabelle tblAnreden enthalten ist, erhalten Sie einen entsprechenden Fehler.

Das bedeutet, dass wir vor dem Wiederherstellen der Tabellen dringend die richtige Reihenfolge zum Wiederherstellen ermitteln und die Wiederherstellung in dieser Reihenfolge durchführen müssen. Die Ermittlung der richtigen Reihenfolge ist der aufwendigere Teil der geplanten Lösung.

Formular als Schaltzentrale

Als Erstes wollen wir ein Formular erstellen, das als Schaltzentrale für die Funktionen zum Sichern und Wiederherstellen der Tabellen dient. Das Formular heißt frmSichernUndWiederherstellen und soll zunächst zwei Schaltflächen im Fußbereich des Formulars enthalten. Die Schaltfläche cmdSichern soll zunächst alle in der Datenbank enthaltenen Tabellen kopieren. Die Schaltfläche cmdWiederherstellen soll die vorhandenen Tabellen mit den Daten aus den gesicherten Tabellen füllen.

Das Formular sieht im derzeitigen Zustand wie in Bild 1 aus.

Erster Entwurf des Formulars frmSichernUndWiederherstellen

Bild 1: Erster Entwurf des Formulars frmSichernUndWiederherstellen

Tabellen sichern

Um die Tabellen zu sichern, benötigen wir eine entsprechende Aktionsabfrage. Diese wollen wir per VBA dynamisch für alle in der Datenbank enthaltenen Tabellen zusammensetzen und ausführen.

Wer unsicher in der Formulierung der entsprechenden SQL-Ausdrücke ist, kann jederzeit die Entwurfsansicht für die Erstellung von Abfragen zuhilfe nehmen. In diesem Fall legen Sie eine neue Abfrage an, fügen dieser etwa die Tabelle tblKategorien hinzu und ziehen alle Felder in das Entwurfsraster (alternativ reicht auch das Hinzufügen des Sternchens stellvertretend für alle Felder der Tabelle).

Dann klicken Sie wie in Bild 2 auf die Schaltfläche Ent-wurf|Ab-frage-typ|Ta-belle er-stellen. Dies ruft die Anzeige des Dialogs aus Bild 3 hervor, mit dem Sie den Namen der zu erstellenden Tabelle angeben und gegebenenfalls eine andere Zieldatenbank festlegen.

Angabe des Namens der zu erstellenden Tabelle

Bild 2: Angabe des Namens der zu erstellenden Tabelle

ändern einer Auswahlabfrage in eine Tabellenerstellungsabfrage

Bild 3: ändern einer Auswahlabfrage in eine Tabellenerstellungsabfrage

Es tut sich zunächst nichts, aber wenn Sie nun in die SQL-Ansicht der soeben erstellten Abfrage wechseln, erhalten Sie einen SQL-Ausdruck, der mit SELECT…INTO beginnt und einer Tabellenerstellungsabfrage entspricht:

SELECT KategorieID, Kategoriename, 
Beschreibung, 
Abbildung 
INTO tblKategorien_Backup
FROM tblKategorien;

Unter Verwendung des Sternchens ginge dies noch kürzer:

SELECT * INTO tblKategorien_Backup
FROM tblKategorien;

VBA-Prozedur zum Sichern der Tabellen

Damit können wir nun bereits die VBA-Prozedur zum Erstellen der Tabellenkopien anlegen. Diese sieht im ersten Anlauf wie in Listing 1 aus. Die Prozedur durchläuft alle TableDef-Objekte der Datenbank und kopiert diese mit der SELECT…INTO-Anweisung in eine neue Tabelle, die mit dem Zusatz _Backup ausgestattet wird.

Private Sub cmdSichern_Click()
     Dim db As DAO.Database
     Dim tdf As DAO.Recordset
     Set db = CurrentDb
     For Each tdf In db.TableDefs
         db.Execute "SELECT * INTO " & tdf.Name & "_Backup FROM " & tdf.Name
     Next tdf
     Set db = Nothing
End Sub

Listing 1: Prozedur zum Sichern aller Tabellen der Datenbank

Der erste Test liefert gleich ein kleines Problem zutage, wie Bild 4 zeigt: Anscheinend enthält eine der zu kopierenden Tabellen ein mehrwertiges Feld, was zu dem angezeigten Fehler führt.

Fehler beim Kopieren einer Tabelle mit Mehrfachwertfeld

Bild 4: Fehler beim Kopieren einer Tabelle mit Mehrfachwertfeld

Mehrwertige Felder sollte man nicht unbedingt nutzen, was aber dennoch nichts an der Frage ändert, in welcher Tabelle sich dieses Feld befindet. Ein Klick auf Debuggen und die Analyse des aktuellen Wertes von tdf.Name liefert die schuldige Tabelle: Es handelt sich um die Systemtabelle MSysResources.

Bei dem Feld, das den Fehler auslöst, handelt es sich genau genommen aber nicht um ein mehrwertiges Feld, sondern um ein Anlagefeld. Diese beiden Feldtypen lassen sich also nicht mit der SELECT…INTO-Anweisung kopieren.

Ein weiterer Fehler kann auftreten, wenn die Datenbank temporäre Tabellen enthält, deren Name mit dem Tilde-Zeichen (~) beginnt (Fehlermeldung s. Bild 5). Beide schließen wir der Einfachheit halber zunächst aus, indem wir der Prozedur eine Bedingung hinzufügen, die nur noch solche Tabellen kopiert, die mit tbl beginnen (s. Listing 2).

Fehler beim Kopieren einer temporären Tabelle

Bild 5: Fehler beim Kopieren einer temporären Tabelle

Auf diese Weise schließen wir aktuell Tabellen aus, die entweder als Systemtabelle oder durch die Tilde (~) als erstes Zeichen als temporäre Tabelle gekennzeichnet sind.

Nun haben wir zwar gesagt, dass man als professioneller Access-Entwickler keine mehrwertigen Felder verwendet (der Grund ist, dass diese Beziehungen in verborgenen Tabellen verwalten).

Allerdings wird man bei Verwendung der neueren Access-Versionen (ab Access 2007) nicht um die Verwendung von Anlage-Feldern herumkommen.

Und diese lösen, wie oben gesehen, ebenfalls Fehler aus, wenn Sie diese per SELECT…INTO-Anweisung in eine neue Tabelle kopieren möchten.

Eines vorweg: Wir haben zwar Möglichkeiten gefunden, die Inhalte von Anlagefeldern von einer in die andere Tabelle zu verschieben, aber das Erstellen von Anlagefeldern per Tabellenerstellungsabfrage scheint nicht möglich zu sein. Deshalb gehen wir einen kleinen Umweg: Wir erstellen die neue Tabelle, die auch die Daten des Anlagefeldes aufnehmen soll, zwar mit SELECT INTO.

Statt das Anlagefeld wie die übrigen Felder direkt zu kopieren, legen wir jedoch nur die in diesem Zusammenhang benötigten Felder der dem Anlagefeld untergeordneten Tabelle an – in diesem Fall FileData und FileName. Die entsprechende SELECT INTO-Abfrage soll dann wie folgt aussehen:

SELECT Data.FileData AS Data_FileData, 
Data.FileName AS Data_FileName, 
Extension, Id, Name, Type 
INTO _tblResources_Backup 
FROM tblResources

Die Zielfelder haben wir mit dem Unterstrich statt mit dem Punkt ausgestattet, weil der Punkt nicht als Teil von Feldnamen verwendet werden darf. Aus Data.FileData wird so in der Backuptabelle Data_FileData. Nach dem Kopieren soll etwa die Tabelle tblResources (welche der Systemtabelle MSysResources entspricht, die wir zu Testzwecken in tblResources kopiert haben) so wie die Tabelle _tblResources_Backup aussehen (s. Bild 6).

Tabellen mit Anlagefeld und die Backup-Tabelle mit den entsprechenden Feldern

Bild 6: Tabellen mit Anlagefeld und die Backup-Tabelle mit den entsprechenden Feldern

Schauen wir uns nun die modifizierte Variante der Prozedur cmdSichern_Click an, die jetzt auch Anlagefelder sichert (s. Listing 3). Diese durchläuft wiederum per For Each-Schleife alle Tabellen der Datenbank und berücksichtigt darin alle Tabellen, die mit tbl beginnen. Die Variable strFelder soll die Liste aller zu berücksichtenden Felder aufnehmen. Mit einem Sternchen (*) für alle Felder ist es nicht getan, da wir ja den Feldern des Anlagefeldes eine Sonderbehandlung angedeihen lassen müssen.

Private Sub cmdSichern_Click()
     Dim db As DAO.Database
     Dim tdf As DAO.TableDef
     Dim fld As DAO.Field
     Dim strFelder As String
     Dim strSQL As String
     Set db = CurrentDb
     For Each tdf In db.TableDefs
         If tdf.Name Like "tbl*" Then
             strFelder = ""
             For Each fld In tdf.Fields
                 Select Case fld.Type
                     Case dbAttachment
                         strFelder = strFelder & ", " & fld.Name & ".FileData AS " _
                             & fld.Name & "_FileData, " & fld.Name & ".FileName AS " _
                             & fld.Name & "_FileName, " & fld.Name & ".FileType AS " _
                             & fld.Name & "_FileType" 
                     Case dbComplexByte, dbComplexDecimal, dbComplexDouble, dbComplexGUID, _
                             dbComplexInteger, dbComplexLong, _
                             dbComplexSingle, dbComplexText
                         MsgBox "Mehrwertige Felder werden nicht unterstützt."
                     Case Else
                         strFelder = strFelder & ", " & fld.Name
                 End Select
             Next fld
             If Len(strFelder) > 0 Then
                 strFelder = Mid(strFelder, 3)
             End If
             strSQL = "SELECT " & strFelder & " INTO _" & tdf.Name & "_Backup FROM " & tdf.Name
             On Error Resume Next
             db.Execute "DROP TABLE _" & tdf.Name & "_Backup", dbFailOnError
             On Error GoTo 0
             db.Execute strSQL, dbFailOnError
         End If
     Next tdf
     Set db = Nothing
End Sub

Listing 3: Kopieren der Inhalte aller Tabellen inklusive Anlagefelder

Die folgende For Each-Schleife durchläuft alle Felder des TableDef-Objekts für die aktuelle Tabelle.

Dabei unterscheidet die Prozedur in einer Select Case-Bedingung nach dem Typ des Feldes. Lautet dieser dbAttachment, handelt es sich um ein Anlagefeld. Für dieses fügt die Prozedur einen Ausdruck wie den folgenden zur Variablen strFelder hinzu:

, Data.FileData AS Data_FileData
, Data.FileName AS Data_FileName

Für alle anderen Felddatentypen wird strFelder einfach nur um ein führendes Komma und den Feldnamen erweitert. Mehrwertige Felder werden nicht berücksichtigt. Wenn eine Tabelle ein mehrwertiges Feld enthält, zeigt die Prozedur lediglich einen entsprechenden Hinweis an.

Wenn strFelder eine Zeichenkette mit einer Länge größer als 0 enthält (was der Regelfall ist, aber sicher ist sicher), schneidet die Prozedur die ersten beiden Zeichen ab, welche dem führenden Komma und einem Leerzeichen entsprechen.

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