Aktuelle Datenbankversion ermitteln

Es gibt verschiedene Gründe, um Kopien einer Datenbank anzulegen. Das Herstellen einer Sicherungskopie ist wohl der am meisten verbreitete Grund. Das ist sinnvoll, aber es kann dabei zu Problemen kommen, wenn man nicht achtsam ist: Dann arbeitet man auf einmal in der Sicherheitskopie weiter und wundert sich, wenn man anschließend die Originaldatenbank öffnet und die Funktionen, die man neu hinzuprogrammiert, nicht mehr findet. Oder man testet mit einer Datenbankdatei auf einer virtuellen Maschine und fügt dort Anpassungen hinzu. Auch hier kann es zu einem ähnlichen Durcheinander kommen. Um in einem solchen Fall die aktuelle Version zu finden, reicht es nicht, sich das Änderungsdatum der Datei anzusehen. Warum das nicht reicht und wie wir die aktuellere Datenbank zuverlässig finden, zeigen wir in diesem Beitrag.

Die Ausgangssituation lässt sich auch wie folgt zusammenfassen: Uns liegen zwei Datenbankdateien gleichen Namens vor, von denen wir nicht mehr wissen, welche den aktuellsten Bearbeitungsstand aufweist. Und leider haben wir auch noch soeben beide Versionen geöffnet, um zu schauen, ob wir so herausfinden können, welche die aktuelle Version ist. Damit haben wir einen wichtigen Marker zerstört – nämlich das Änderungsdatum der Datei.

Daran hätten wir zumindest erkennen können, welche Version wir zuletzt geöffnet haben. Allerdings wird bereits beim Öffnen einer Access-Anwendung das Änderungsdatum auf das aktuelle Datum eingestellt – wir brauchen dazu gar keine Änderungen am Entwurf oder an den Daten vorzunehmen.

Nun haben wir also zwei verschiedene Versionen einer Datenbank vorliegen und benötigen Kriterien, um herauszufinden, an welcher wir zuletzt gearbeitet haben.

Diese lauten beispielsweise wie folgt:

  • Gibt es in einer der beiden Datenbanken neue, die es in der anderen Datenbank nicht gibt?
  • Wurden aus einer der Datenbanken vielleicht sogar Objekte gelöscht?
  • Wie lautet das letzte Bearbeitungsdatum der Objekte der Datenbank?

Wenn wir diese Kriterien untersuchen, sollten wir annähernd herausfinden können, welche der beiden Datenbankdateien den aktuelleren Stand aufweist. Das ist zumindest dann der Fall, wenn wir nur an einer der beiden Änderungen seit einem bestimmten Zeitpunkt vorgenommen haben.

Wenn wir die Datenbanken abwechselnd geöffnet und Änderungen an verschiedenen (oder sogar den gleichen) Stellen vorgenommen haben, ist das zwar unangenehm, weil wir die Änderungen dann zusammenführen müssen. Wir wissen dann aber zumindest, warum bestimmte Änderungen, von denen wir sicher waren, dass wir sie durchgeführt haben, nicht mehr vorhanden sind.

Änderungszeitpunkt von Objekten ermitteln

Den Änderungszeitpunkt eines Objekts können wir neben dem Erstellungszeitpunkt über die Benutzeroberfläche von Access ermitteln. Dazu klicken wir mit der rechten Maustaste in die Titelleiste des Navigationsbereichs von Access und wählen dort den Eintrag Anzeigen nach|Details aus. Danach sehen wir die gewünschten Informationen zu jedem Eintrag (siehe Bild 1).

Änderungszeitpunkt von Objekten ermitteln

Bild 1: Änderungszeitpunkt von Objekten ermitteln

Wenn wir Datenbanken mit so wenigen Objekten wie die aus dem Screenshot vergleichen wollen, brauchen wir dafür keine eigene Anwendung zu entwickeln. Aber in der Regel ist die Anzahl der enthaltenen Objekte größer und ein manueller Abgleich macht schnell keinen Spaß mehr.

Also machen wir uns auf die Suche nach dem Speicherort dieser Informationen und stoßen dabei schnell auf die Systemtabelle MSysObjects. Systemtabellen wie diese sind standardmäßig ausgeblendet. Wir holen diese hervor, indem wir wieder mit der rechten Maustaste auf die Titelleiste des Navigationsbereichs klicken und nun den Eintrag Navigationsoptionen… auswählen. Es erscheint der Dialog Navigationsoptionen, wo wir im Bereich Anzeigeoptionen die Optionen Ausgeblendete Objekte anzeigen und Systemobjekte anzeigen aktivieren.

Diese Tabelle liefert für alle Datenbankobjekte wie Tabellen, Abfragen, Formulare, Berichte, Makros und VBA-Module jeweils einen Eintrag (und für einige weitere Objekttypen, die hier nicht von Interesse sind). Dieser enthält neben dem Objekttyp und dem Namen auch zwei Felder namens DateCreated und DateUpdated. Damit haben wir bereits alle Informationen, die wir benötigen. Wir müssen sie nur noch zusammenführen und abgleichen.

Objektedaten in neue Datenbank importieren

Dazu erstellen wir eine neue Datenbank namens AktuellereDatenbankFinden.accdb. In diese wollen wir zuerst die Tabelle MSysObjects der beiden Versionen der Datenbank importieren. Dazu benötigen wir bereits ein Formular, denn wir wollen die Auswahl der Datenbankdateien so komfortabel wie möglich gestalten – in diesem Fall mit entsprechenden Dateiauswahl-Dialogen.

Nach der Auswahl der abzugleichenden Datenbanken wollen wir mit einer Schaltfläche einen Vorgang starten, bei dem die relevanten Daten für alle in den beiden Datenbanken enthaltenen Objekte in eine zusammenfassende Tabelle geschrieben werden.

Diese heißt tblObjekte und ihr Entwurf sieht wie in Bild 2 aus. Neben dem Objektnamen speichern wir darin den Objekttyp sowie das Änderungsdatum für das Objekt in der ersten und in der zweiten Datenbank. Wenn das Objekt nur in einer der beiden Datenbanken vorhanden ist, schreiben wir das Datum nur in das entsprechende Feld.

Entwurf der Tabelle zum Speichern der Änderungsdaten der Objekte

Bild 2: Entwurf der Tabelle zum Speichern der Änderungsdaten der Objekte

Formular zum Anzeigen der Änderungsdaten

Das Formular zur Auswahl der zu untersuchenden Dateien und zum Anzeigen der relevanten Daten legen wir unter dem Namen frmVergleichen an. Diesem fügen wir zunächst zwei Textfelder namens txtVersion1 und txtVersion2 hinzu. Daneben platzieren wir zwei Schaltflächen.

Diese statten wir jeweils mit einem Ordner-Icon aus und stellen die Eigenschaften Hintergrundart und Rahmenart auf Transparent ein. Das Formular sieht anschließend wie in Bild 3 aus.

Felder zum Auswählen der zu vergleichenden Dateien

Bild 3: Felder zum Auswählen der zu vergleichenden Dateien

Für die Ereignisprozeduren der beiden Schaltflächen, die durch das Ereignis Beim Klicken ausgelöst werden, hinterlegen wir die folgenden Prozeduren:

Private Sub cmdDateiOeffnen1_Click()
     Me!txtVersion1 = DateiOeffnen
End Sub
Private Sub cmdDateiOeffnen2_Click()
     Me!txtVersion2 = DateiOeffnen
End Sub

Beide nutzen die gleiche Funktion, um den Pfad zu der zu untersuchenden Datei zu ermitteln:

Private Function DateiOeffnen() As String
     Dim objFiledialog As Office.FileDialog
     Dim varFilename As Variant
     Set objFiledialog = _
         Application.FileDialog(msoFileDialogFilePicker)
     objFiledialog.InitialFileName = CurrentProject.Path
     If objFiledialog.Show = True Then
         DateiOeffnen = objFiledialog.SelectedItems(1)
     End If
End Function

Um das FileDialog-Objekt zu nutzen, benötigen wir einen Verweis auf die Bibliothek Microsoft Office 16.0 Object Library, den wir mit dem Verweise-Dialog aus Bild 4 hinzufügen (VBA-Editor, Menüeintrag Extras|Verweise). Für die beiden Textfelder stellen wir die Eigenschaft Horizontaler Anker auf Beide ein, damit sich diese an die Formularbreite anpassen.

Hinzufügen eines Verweises auf die Office-Bibliothek

Bild 4: Hinzufügen eines Verweises auf die Office-Bibliothek

Unterformular zum Anpassen der Ergebnisse

Bevor wir die Funktion zum Abgleichen der Unterschiede anlegen, fügen wir dem Formular ein Unterformular zum Anzeigen der Ergebnisse hinzu. Das Unterformular heißt sfmVergleichen und verwendet die Tabelle tblObjekte als Datensatzquelle. Wir fügen alle Felder dieser Tabelle zum Formular hinzu und stellen seine Eigenschaft Standardansicht auf Datenblatt ein (siehe Bild 5).

Unterformular der Anwendung

Bild 5: Unterformular der Anwendung

Danach fügen wir das Formular samt einer weiteren Schaltfläche namens cmdVergleichen zum Hauptformular hinzu (siehe Bild 6). Für das hinzugefügte Unterformular stellen wir die beiden Eigenschaften Horizontaler Anker und Vertikaler Anker auf Beide ein.

Das Hauptformular der Anwendung in der Entwurfsansicht

Bild 6: Das Hauptformular der Anwendung in der Entwurfsansicht

Einlesen der Objektdaten der Datenbanken

Im nächsten Schritt fügen wir der Schaltfläche cmdVergleichen eine Prozedur für das Ereignis Beim Klicken hinzu (siehe Listing 1).

Private Sub cmdVergleichen_Click()
     Dim db As DAO.Database
     Dim rst As DAO.Recordset
     Dim lngObjektID As Long
     Dim strObjekttyp As String
     Dim strWhere As String
     Set db = CurrentDb
     On Error Resume Next
     db.Execute "DELETE FROM tblObjekte", dbFailOnError
     db.Execute "DROP TABLE tblObjects1", dbFailOnError
     db.Execute "DROP TABLE tblObjects2", dbFailOnError
     On Error GoTo 0
     DoCmd.TransferDatabase acImport, "Microsoft Access", Me.txtVersion1, acTable, "MSysObjects", "tblObjects1"
     DoCmd.TransferDatabase acImport, "Microsoft Access", Me.txtVersion2, acTable, "MSysObjects", "tblObjects2"
     strWhere = " WHERE Type IN (1, 4, 5, 6, -32761, -32764, -32766, -32768) AND NOT Name LIKE ''~*'' " _
         & "AND NOT Name LIKE ''MSys*'' AND NOT Name LIKE ''f_*''"
     Set rst = db.OpenRecordset("SELECT * FROM tblObjects1" & strWhere, dbOpenDynaset)
     Do While Not rst.EOF
         strObjekttyp = ObjekttypErmitteln(rst!Type)
         db.Execute "INSERT INTO tblObjekte(Objektname, Objekttyp, Geaendert1) VALUES(''" & rst!Name & "'', ''" _
             & strObjekttyp & "'', " & ISODatum(rst!DateUpdate) & ")", dbFailOnError
         rst.MoveNext
     Loop
     Set rst = db.OpenRecordset("SELECT * FROM tblObjects2" & strWhere, dbOpenDynaset)
     Do While Not rst.EOF
         lngObjektID = Nz(DLookup("ObjektID", "tblObjekte", "Objektname = ''" & rst!Name & "''"), 0)
         If lngObjektID = 0 Then
             strObjekttyp = ObjekttypErmitteln(rst!Type)
             db.Execute "INSERT INTO tblObjekte(Objektname, Objekttyp, Geaendert2) VALUES(''" & rst!Name & "'', ''" _
                 & strObjekttyp & "'', " & ISODatum(rst!DateUpdate) & ")", dbFailOnError
         Else
             db.Execute "UPDATE tblObjekte SET Geaendert2 = " & ISODatum(rst!DateUpdate) & " WHERE ObjektID = " _
                 & lngObjektID, dbFailOnError
         End If 
         rst.MoveNext
     Loop
     Me!sfmVergleichen.Form.Requery
End Sub

Listing 1: Prozedur zum Abgleichen der Änderungsdaten der Objekt der zu untersuchenden Datenbanken

Hier führen wir zuerst einige Aufräumarbeiten durch. Wir leeren die Tabelle tblObjekte, damit keine Daten aus vorherigen Untersuchungen mehr in der Tabelle sind. Außerdem löscht die Prozedur zwei Tabellen namens tblObjects1 und tblObjects2, die wir bisher noch gar nicht angelegt haben.

Genau aus diesem Grund erledigen wir diese Aufgabe auch bei deaktivierter eingebauter Fehlerbehandlung – der Versuch, eine nicht vorhandene Tabelle zu löschen, würde sonst einen Fehler auslösen.

Was aber sind das überhaupt für Tabellen? In diese kopieren wir die Daten der Systemtabellen namens MSysObjects der zu untersuchenden Datenbanken. Das erledigen wir mit der Methode TransferDatabase der DoCmd-Klasse. Damit diese einen Import durchführt, übergeben wir als ersten Parameter den Wert acImport. Der zweite Parameter gibt die Herkunftsanwendung der Daten an, der dritte den Typ des einzulesenden Objekts. Die letzten beiden Parameter legen fest, dass die Daten der Tabelle MSysObjects in die Tabelle tblObjects1 beziehungsweise tblObjects2 eingelesen werden sollen. Dieser Befehl erstellt die Tabellen auf Basis der Quelltabelle neu.

Abgleich der Änderungsdaten

Die folgenden Anweisungen legen die für den Abgleich der Änderungsdaten notwendigen Daten in der Tabelle tblObjekte an. Dabei wollen wir nur die Datensätze der Tabelle MSysObjects berücksichtigen, die eines der zu untersuchenden Objekte betreffen. Außerdem wollen wir Systemtabellen und temporäre Objekte oder im Hintergrund verwendete Objekte nicht berücksichtigen.

Dazu stellen wir ein Where-Kriterium zusammen, das nur die Objekttypen 1, 4, 5, 6, -32761, -32764, -32766 und -32768 berücksichtigt (mehr dazu weiter unten) und einige Objekte aufgrund ihres Objektnamens ausschließt (f_*, MSys* oder ~*).

Nun beginnen wir mit dem Einlesen der Daten aus der ersten zu untersuchenden Datenbank. Dazu erstellen wir ein Recordset auf Basis der Tabelle tblObjects1, welches das Kriterium aus strWhere berücksichtigt. Das Recordset enthält also nur die Objekte der Datenbank, die wir untersuchen wollen. Anschließend durchlaufen wir die Elemente des Recordsets in einer Do While-Schleife. Dabei rufen wir als Erstes eine Funktion namens ObjekttypErmitteln auf, der wir den Wert des Feldes Type übergeben. Die Funktion untersucht diesen Wert in einer Select Case-Bedingung und liefert einen Text zurück, welcher dem angegebenen Zahlenwert entspricht – bei dem Wert 1 beispielsweise den Text Tabelle:

Private Function ObjekttypErmitteln(lngObjekttyp As _
         Long) As String
     Select Case lngObjekttyp
         Case 1
             ObjekttypErmitteln = "Lokale Tabelle"
         Case 4
             ObjekttypErmitteln = "ODBC-Tabelle"
         Case 5
             ObjekttypErmitteln = "Abfrage"
         Case 6
             ObjekttypErmitteln = "Verknüpfte Tabelle"
         Case -32761
             ObjekttypErmitteln = "VBA-Modul"
         Case -32764
             ObjekttypErmitteln = "Bericht"
         Case -32766
             ObjekttypErmitteln = "Makro"
         Case -32768
             ObjekttypErmitteln = "Formular"
     End Select
End Function

Damit ausgestattet rufen wir die Execute-Methode des Database-Objekts der aktuellen Datenbank auf und übergeben dieser eine INSERT INTO-Anweisung, mit der wir einen neuen Datensatz zur Tabelle tblObjekte hinzufügen.

Dieser trägt die Werte für die Felder Objektname, Objekttyp und Geaendert1 ein, also das Änderungsdatum für das Objekt aus der ersten zu untersuchenden Datenbank.

Nach dem Durchlaufen der ersten Schleife sieht die Tabelle wie in Bild 7 aus. Für jedes relevante Objekt wurde ein Eintrag angelegt. Lediglich das Änderungsdatum für das gleichnamige Objekt in der zweiten zu untersuchenden Datenbank ist noch nicht vorhanden.

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