Datenbankexport

Wenn Sie ein Abonnement des Magazins 'Access im Unternehmen' besitzen, können Sie sich anmelden und den kompletten Artikel lesen.
Anderenfalls können Sie das Abonnement hier im Shop erwerben.

Datenbankexport nach Maß

Wenn Sie eine Datenbank entwickeln, die Sie an einen oder mehrere Kunden weitergeben, enthält diese oft noch Testdaten, Tools, die Sie zur Entwicklung benötigen et cetera. Vielleicht entwickeln Sie sogar Datenbanken für mehrere Zielversionen von Access, die unterschiedliche Anforderungen haben. Oder Sie wollen schnell Versionen mit unterschiedlichen Funktionsumgängen exportieren. Die hier vorgestellte Lösung zeigt einige Techniken, um dies zu bewerkstelligen. Der Vorteil etwa gegenüber den eingebauten Importieren-Funktionen ist, dass Sie hier vollständige Konfigurationen speichern können.

Gerade in der Startphase einer Anwendung, die Sie für einen oder mehrere Kunden oder Arbeitsplätze entwickeln, werden Sie des Öfteren neue Versionen mit Erweiterungen oder Fehlerkorrekturen ausliefern. Wenn Sie die Anwendung dabei von Objekten befreien möchten oder diese in verschiedenen Fassungen herausgeben möchten, steht mit jeder neuen Version eine Menge Handarbeit an, um aus der Entwicklungsversion die Version für den Kunden zu extrahieren.

Und es gibt eine ganze Reihe von Anwendungsfällen, die unsere Lösung, deren Benutzeroberfläche etwa wie in Bild 1 aussieht, enthalten könnte:

Der Export-Helfer für Access-Datenbanken

Bild 1: Der Export-Helfer für Access-Datenbanken

  • Auswahl von Tabellen und Angabe, ob diese mit oder ohne Daten exportiert werden sollen
  • Auswahl von Abfragen
  • Auswahl von Formularen
  • Auswahl von Berichten
  • Auswahl von Makros
  • Auswahl von Modulen
  • Suchen und Ersetzen in den Daten von Tabellen in Modulen, um Testdaten zu löschen oder durch Dummy-Daten zu ersetzen
  • Übertragen von Verweisen
  • Übertragen von Ribbon-Definitionen
  • Übertragen von Menüleisten
  • Ausführen von SQL-Anweisungen für spezielle Anforderungen, die auf die Zielanwendung angewendet werden sollen
  • Übertragen von Eigenschaften
  • Verschiedene Access-Versionen als Ziel

Nicht alle Funktionen werden in diesem Beitrag beziehungsweise in der mit diesem Beitrag veröffentlichten Lösung umgesetzt.

Art der Lösung

Diese Lösung ließe sich prima als Add-In programmieren und dann von allen Datenbanken aus aufrufen, die exportiert werden sollen. Allerdings beziehen sich die Konfigurationsdaten jeweils auf die eine Datenbank, auf deren Objekte und Eigenschaften der Export zugeschnitten ist – daher haben wir uns entschieden, die Lösung als Satz von Objekten zu programmieren, die Sie in die Zieldatenbank importieren. Dort können Sie in den dazugehörenden Tabellen dann problemlos genau die Konfiguration für die jeweilige Datenbank speichern.

Die Lösung besteht aus einigen Tabellen, Formularen und Modulen, die alle mit dem üblichen Präfix beginnen, dann den Text Export_ und schließlich die eigentliche Bezeichnung des Inhalts enthalten (bei Unterformularen zusätzlich sfm).

Dies sind die Tabellen, die Sie in die Zieldatenbank importieren müssen, um die Lösung darin zu nutzen:

  • tblExport_Exporte
  • tblExport_Objekte
  • tblExport_ObjekteExporte
  • tblExport_Objekttypen
  • tblExport_Optionen
  • tblExport_SQLAnweisungen
  • tblExport_SQLAnweisungenExporte
  • tblExport_SuchenErsetzen
  • tblExport_Verweise
  • tblExport_VerweiseExporte
  • tblExport_Zielversionen

Und diese Formulare benötigen Sie:

  • frmExport_Main
  • frmExport_sfmOptionen
  • frmExport_sfmAbfragen
  • frmExport_sfmBerichte
  • frmExport_sfmFormulare
  • frmExport_sfmMakros
  • frmExport_sfmModule
  • frmExport_sfmSQLAnweisungen
  • frmExport_sfmSuchenErsetzen
  • frmExport_sfmTabellen
  • frmExport_sfmVerweise

Außerdem benötigen Sie noch das Modul mdlExport_Tools.

Basis: Die Konfiguration

Die Basis der in der Lösung verwalteten Daten bildet eine einzelne Konfiguration. Eine neue Konfiguration legen Sie mit der Schaltfläche rechts neben dem Kombinationsfeld Bezeichnung an. Vorhandene Konfigurationen wählen Sie mit diesem Kombinationsfeld aus – s. Bild 2. Dieses Kombinationsfeld bezieht seine Daten aus der Tabelle tblExporte_Exporte, deren Entwurf Sie in Bild 3 finden.

Auswahl einer Konfiguration

Bild 2: Auswahl einer Konfiguration

Entwurf der Tabelle zum Speichern der Konfigurationen

Bild 3: Entwurf der Tabelle zum Speichern der Konfigurationen

Das Feld Exportbezeichnung dieser Tabelle ist mit einem eindeutigen Index versehen, damit der Benutzer keine Bezeichnung doppelt anlegen kann.

Das Kombinationsfeld cboBezeichnungen verwendet die folgende Abfrage als Datensatzherkunft:

SELECT ExportID, ­Exportbezeichnung 
FROM tblExport_Exporte 
ORDER BY Exportbezeichnung;

Damit es nur die zweite Spalte des Abfrageergebnisses anzeigt, stellen Sie die Eigenschaft Spaltenanzahl auf den Wert 2 und Spaltenbreiten auf 0cm ein.

Die Schaltfläche rechts vom Kombinationsfeld dient dazu, einen neuen Export und somit einen neuen Eintrag in der Tabelle tblExport_Exporte anzulegen.

Sie löst die folgende Prozedur aus:

Private Sub cmdNeuerExport_Click()
     ExportAnlegen
End Sub

Die hier aufgerufene Funktion ExportAnlegen erledigt die eigentliche Arbeit (s. Listing 1). Sie fragt zunächst mit einer InputBox die Bezeichnung für die neue Exportkonfiguration ab und speichert das Ergebnis in der Variablen strExport. Eine per Execute-Methode ausgeführte INSERT INTO-SQL-Abfrage legt dann einen neuen Datensatz mit der soeben ermittelten Bezeichnung in der Tabelle tblExport_Exporte an. Die folgende Anweisung ermittelt dann mit SELECT @@IDENTITY den Primärschlüsselwert des neuen Datensatzes und schreibt diesen in die Variable lngExportID.

Private Function ExportAnlegen() As Long
     Dim strExport As String
     Dim db As DAO.Database
     Dim lngExportID As Long
     Set db = CurrentDb
     strExport = InputBox("Bitte geben Sie eine Bezeichnung für den Export ein.", "Exportbezeichnung", "[Exportbezeichnung]")
     db.Execute "INSERT INTO tblExport_Exporte(Exportbezeichnung) VALUES('" & strExport & "')", dbFailOnError
     lngExportID = db.OpenRecordset("SELECT @@IDENTITY").Fields(0)
     Me!cboBezeichnungen.Requery
     Me!cboBezeichnungen = lngExportID
     Me.Requery
     Me.Recordset.FindFirst "ExportID = " & lngExportID
End Function

Listing 1: Diese Prozedur fragt die notwendigen Informationen zum Anlegen eines neuen Exports ab.

Dann aktualisiert die Prozedur die Datensatzherkunft des Kombinationsfeldes und stellt dieses auf den neuen Eintrag ein. Auch die Datenherkunft des Formulars wird aktualisiert und entsprechend mit FindFirst auf den neuen Datensatz eingestellt.

Datenherkunft des Formulars

Damit kommen wir zur Datenherkunft des Formulars frmExport_Exporte selbst. Diese besteht schlicht und einfach aus der Tabelle tblExport_Exporte.

Beim Laden des Formulars

Gleich nach dem Öffnen des Formulars wird das Ereignis Beim Laden ausgelöst. Die dadurch aufgerufene Prozedur sieht wie folgt aus:

Private Sub Form_Load()
     If IsNull(Me!ExportID) Then
         ExportAnlegen
     End If
     ObjekteEinlesen
     Me!cboBezeichnungen = Me!ExportID
End Sub

Hier findet sich also ein weiterer Aufruf der Funktion ExportAnlegen, der aber nur aufgerufen wird, wenn das Feld ExportID den Wert Null enthält. Dies ist aber nur der Fall, wenn die Datenherkunft des Formulars noch gar keinen Datensatz enthält – also dann, wenn Sie die Objekte der Lösung frisch in die Zielanwendung importiert haben und die Tabelle tblExport_Exporte leer ist.

Anschließend ruft die Prozedur die Routine ObjekteEinlesen auf, welche – kurz zusammengefasst – alle Objekte der Datenbank ermittelt und in einige weitere Tabellen einträgt.

Schließlich stellt die Prozedur das Kombinationsfeld cboBezeichnungen auf den aktuellen Wert des Feldes ExportID des Formulars ein. Auf diese Weise werden Daten im Formular und im Kombinationsfeld synchronisiert.

Objekte einlesen

Die Prozedur ObjekteEinlesen wird sowohl beim Öffnen des Formulars ausgelöst als auch durch eine eigene Schaltfläche im Fuß des Formulars. Dies dient dem Benutzer als Möglichkeit, die im Formular angezeigten Daten etwa nach dem Anlegen oder Löschen von Objekten zu aktualisieren. Die Schaltfläche heißt cmdObjekteAktualisieren und enthält lediglich den Aufruf der Routine ObjekteEinlesen:

Private Sub cmdObjekteAktualisieren_ Click()
     ObjekteEinlesen
End Sub

Die Prozedur ObjekteEinlesen selbst finden Sie in Listing 2. Die Prozedur füllt eine Database-Variable mit einem Verweis auf die aktuelle Datenbank und speichert die ID des aktuell angezeigten Exports in der Variablen lngExportID. Dann beginnt sie mit der Bearbeitung einer Tabelle, die wir bisher noch nicht vorgestellt haben. Sie heißt tblExport_Objekte und erfasst alle in der aktuellen Datenbank enthaltenen Objekte. Den Entwurf finden Sie in Bild 4, die Erläuterungen dazu folgen gleich im Anschluss.

Entwurf der Tabelle zum Speichern der Objekte

Bild 4: Entwurf der Tabelle zum Speichern der Objekte

Private Sub ObjekteEinlesen()
     Dim db As DAO.Database
     Dim lngExportID As Long
     Set db = CurrentDb
     lngExportID = Me!ExportID
     db.Execute "UPDATE tblExport_Objekte AS t1 INNER JOIN " _
         & "tblExport_ObjekteExporte AS t2 ON t1.ObjektID = t2.ObjektID " _
         & "SET t1.Loeschen = -1 WHERE t2.ExportID = " & Me!ExportID, dbFailOnError
     TabellenEinlesen db, lngExportID
     AbfragenEinlesen db, lngExportID
     FormulareEinlesen db, lngExportID
     BerichteEinlesen db, lngExportID
     MakrosEinlesen db, lngExportID
     ModuleEinlesen db, lngExportID
     VerweiseEinlesen db, lngExportID
     db.Execute "DELETE FROM tblExport_Objekte WHERE Loeschen = -1", dbFailOnError
     Me.Requery
     Me.Recordset.FindFirst "ExportID = " & lngExportID
End Sub

Listing 2: Diese Routine startet das Einlesen aller Objekte der aktuellen Datenbank

Die Prozedur aktualisiert nun zunächst eventuell in der Tabelle enthaltene Datensätze, indem sie den Wert des Feldes Loeschen für jeden Datensatz auf den Wert -1 setzt, was dem Wert True entspricht. Der Hintergrund ist, dass die folgenden Prozeduren ja nicht nur alle vorhandenen Objekte in die Tabelle tblExport_Objekte eintragen sollen, sondern dass eventuell gelöschte Objekte, die aber noch in der Tabelle enthalten sind, dort entfernt werden sollen. Anschließend prüfen die folgenden Methoden, ob ein in der Datenbank enthaltenes Objekt bereits in der Tabelle tblExport_Objekte enthalten ist. Falls ja, wird das Feld Geloescht für dieses Objekt auf den Wert False einstellt, falls nicht, wird ein neuer Datensatz für das Objekt angelegt. Datensätze, die dann noch den Wert True im Feld Geloescht aufweisen, sind offensichtlich nicht mehr in der Tabelle enthalten (oder unter einem anderen Namen gespeichert) und können gelöscht werden.

Schließlich aktualisiert die Prozedur die Datenherkünfte des Formulars und somit auch die der Unterformulare mit der Requery-Methode und stellt das Formular auf den zuletzt aktiven Datensatz ein.

Bei dieser Gelegenheit wird auch das Ereignis Beim Anzeigen ausgelöst, das noch das Kombinationsfeld cboBezeichnungen auf den aktuellen Datensatz des Formulars einstellt:

Private Sub Form_Current()
     Me!cboBezeichnungen = Me!ExportID
End Sub

Tabellen einlesen

Die Prozedur TabellenEinlesen wird von der zuvor beschriebenen Prozedur ObjekteEinlesen aufgerufen und erwartet zwei Parameter – einen Verweis auf die zu untersuchende Datenbank sowie den Primärschlüsselwert für den aktuellen Export (s. Listing 3).

Private Sub TabellenEinlesen(db As DAO.Database, lngExportID As Long)
     Dim tdf As DAO.TableDef
     Dim i As Integer
     Dim strFilter() As String
     Dim strFilterstring As String
     Dim bolEinlesen As Boolean
     Dim lngObjektID As Long
     strFilterstring = Nz(DLookup("Tabellenfilter", "tblExport_Exporte", "ExportID = " & lngExportID), "*")
     strFilter = Split(strFilterstring, ",")
     For Each tdf In db.TableDefs
         bolEinlesen = False
         For i = LBound(strFilter) To UBound(strFilter)
             If tdf.Name Like strFilter(i) Then
                 bolEinlesen = True
                 Exit For
             End If
         Next i
         For i = LBound(strFilter) To UBound(strFilter)
             If "-" & tdf.Name Like strFilter(i) And Left(strFilter(i), 1) = "-" Then
                 bolEinlesen = False
                 Exit For
             End If
         Next i
         If bolEinlesen = True Then
             On Error Resume Next
             db.Execute "INSERT INTO tblExport_Objekte(Objektbezeichnung, ObjekttypID, Loeschen) VALUES('" & tdf.Name _
                 & "', 1, 0)", dbFailOnError
             If Err.Number = 3022 Then
                 lngObjektID = DLookup("ObjektID", "tblExport_Objekte", "Objektbezeichnung = '" & tdf.Name _
                     & "' AND ObjekttypID = 1")
                 db.Execute "UPDATE tblExport_Objekte SET Loeschen = 0 WHERE ObjektID = " & lngObjektID, dbFailOnError
             Else
                 lngObjektID = db.OpenRecordset("SELECT @@IDENTITY").Fields(0)
             End If
             db.Execute "INSERT INTO tblExport_ObjekteExporte(ObjektID, ExportID) VALUES(" & lngObjektID & ", " _
                 & lngExportID & ")", dbFailOnError
             On Error GoTo 0
         End If
     Next tdf
End Sub

Listing 3: Einlesen der Tabellen der aktuellen Datenbank und eintragen in die Tabelle tblExport_Objekte

Die Prozedur soll nicht alle Tabellen einlesen, sondern nur solche, die nicht über spezielle Optionen von der Erfassung ausgeschlossen werden sollen – mehr zum Dialog, mit dem Sie diese Optionen festlegen, weiter unten.

Deshalb ermittelt die Prozedur nun zunächst einen entsprechenden Filterstring, der im Feld Tabellenfilter der Tabelle tblExport_Exporte für den Datensatz mit dem entsprechenden Wert im Fremdschlüsselfeld ExportID gespeichert ist. Der Wert, der hier ermittelt und in der Variablen strFilterstring gespeichert wird, könnte etwa wie folgt aussehen (wenn das Feld Tabellenfilter leer ist, wird strFilterstring mit dem Ausdruck * gefüllt):

tbl*,usys*,msysresources,-frmExport_*

Das bedeutet, dass alle Tabellen, die mit tbl und usys beginnen, und die Tabelle msysresources berücksichtigt werden sollen. Die Tabellennamen, die mit einem vorangestellten Minuszeichen versehen wurden, sollen nicht berücksichtigt werden. In diesem Fall sollen also beispielsweise die Tabellen dieser Lösung, deren Namen alle mit tblExport_ beginnen, nicht eingelesen werden.

Die einzelnen Elemente aus strFilterstring werden nun mit der Split-Funktion an der Stelle der Kommata (,) auseinander genommen und jeweils als eigenes Element zum Array strFilter() hinzugefügt.

Die Prozedur durchläuft nun die TableDefs-Auflistung der aktuellen Datenbank in einer For Each-Schleife, wobei die Variable tdf jeweils die aktuelle Tabelle referenziert.

Für jedes Element durchläuft die Prozedur dann in einer untergeordneten For...Next-Schleife alle Einträge des Arrays mit den Filterausdrücken. Stimmt der Name der aktuellen Tabelle mit dem Filterausdruck überein, stellt die Prozedur die Variable bolEinlesen auf den Wert True ein und verlässt die Schleife. Nun kann es sein, dass etwa der Eintrag tblExport_Exporte dem Kriterium tbl* entspricht, aber auch -tblExport_*. Dann durchläuft die Prozedur im nächsten Schritt in einer weiteren Schleife alle Filterausdrücke und stellt dem Tabellennamen diesmal ein Minuszeichen voran (also etwa -tblExport_Exporte). Liegt hier ein Treffer vor, wird bolEinlesen wieder auf False eingestellt und die Schleife wird ebenfalls mit Exit For verlassen.

Die folgende If...Then-Bedingung prüft den Wert von bolEinlesen für dieses TableDef-Objekt. Ist dieser True, versucht die Prozedur, den Tabellennamen in die Tabelle tblExport_Objekte einzutragen – samt dem Wert False im Feld Loeschen. Ist bereits eine Tabelle gleichen Namens vorhanden, bleibt es bei dem Versuch – die resultierende Fehlermeldung wird durch das vorhergehende On Error Resume Next unterbunden. In diesem Fall ermittelt die Prozedur den Primärschlüsselwert für diesen Eintrag und aktualisiert für den gefundenen Datensatz das Feld Loeschen auf den Wert False. Diese Tabelle ist also bereits in tblExport_Objekte gespeichert, es muss nur noch das zuvor auf True eingestellte Feld Loeschen auf False geändert werden, weil die Tabelle ja noch vorhanden ist. War der Eintrag für diese Tabelle noch nicht vorhanden, ermittelt die Prozedur nun den Primärschlüsselwert des neuen Eintrags und speichert diesen in der Variablen lngObjektID.

Möglicherweise fragen Sie sich, warum wir diesen aufwendigen Weg gehen und nicht einfach alle Objekte vor dem erneuten Einlesen löschen. Der Grund ist einfach: Wir sind faul und wollen unnötige Schritte nicht wiederholen, und wir gehen davon aus, dass dies auch bei Ihnen der Fall ist. In diesem Fall wird ja später festgelegt, welche Objekte mit welcher Exportkonfiguration verknüpft werden. Damit wir diese Information nicht nach jedem Einlesen erneut angeben müssen, werden vorhandene Objekte beibehalten und nur nicht mehr vorhandene Objekte aus den Konfigurationstabellen gelöscht.

Dies war die Leseprobe dieses Artikels.
Melden Sie sich an, um auf den vollständigen Artikel zuzugreifen.

Bitte geben Sie die Zeichenfolge in das nachfolgende Textfeld ein

Die mit einem * markierten Felder sind Pflichtfelder.

Ich habe die Datenschutzbestimmungen zur Kenntnis genommen.