Zur Hauptseite ... Zum Onlinearchiv ... Zum Abonnement ... Zum Newsletter ... Zu den Tools ... Zum Impressum ... Zum Login ...

Gedrucktes Heft

Diesen Beitrag finden Sie in Ausgabe 3/2007.

Unser Angebot für Sie!

Lesen Sie diesen Beitrag und 500 andere sofort im Onlinearchiv, und erhalten Sie alle zwei Monate brandheißes Access-Know-how auf 72 gedruckten Seiten! Plus attraktive Präsente, zum Beispiel das bald erscheinende Buch 'Access 2010 - Das Grundlagenbuch für Entwickler'!

Diesen Beitrag twittern

Zusammenfassung

Erzeugen Sie temporäre Tabellen in automatisch generierten temporären Datenbanken und vermeiden Sie das Aufblähen des Frontends.

Techniken

Datenmodellierung, VBA

Voraussetzungen

Access 2000 und höher

Beispieldatei

TemporaryDatabases.mdb

Shortlink

461

Temporäre Datenbanken

Uwe Ricken, Erzhausen

Sicherlich kennen Sie auch die Situation, dass Sie häufig verwendete statische Daten wie zum Beispiel Einträge für Kombinationslistenfelder oder Listenfelder aus Tabellen erhalten, die im Backend gespeichert sind. Sollte sich das Backend dann auch noch auf einem Server befinden, kann sich das Öffnen etwas verzögern. Lernen Sie in diesem Artikel die Vorzüge der Verwendung von temporären Datenbanken kennen.

Wozu temporäre Datenbanken?

Bei der Entwicklung von Datenbanksystemen kommt es immer wieder vor, dass man statische Elemente wie Parameter, Meldungstexte oder Listeneinträge benötigt oder zur Laufzeit temporäre Objekte für die Berechnung von Zwischenergebnissen erstellt, die anschließend in Berichten weiterverwendet werden sollen.

Eine langsame Netzwerkverbindung erweist sich dabei ebenso als Nachteil wie das Aufblähen des Frontends durch das Erstellen temporärer Objekte, was sich nur durch periodische Komprimierungen regulieren lässt.

Mit einer temporären Datenbank auf dem lokalen Rechner, die beim Start der Anwendung angelegt und nach der Sitzung wieder gelöscht wird, verringern Sie diese Probleme.

Grundsätzliche Funktionsweise

Das Prinzip veranschaulicht Abb. 1. Die Applikation legt beim Start zunächst die temporäre Datenbank in einem dafür vorgesehenen Verzeichnis an und erstellt alle temporären Tabellen mit den passenden Feldern und Indizes und verbindet diese mit der Benutzerschnittstelle (1).

Konzept der temporären Datenbanken.tif

Abb. 1: Konzept der temporären Datenbanken

Anschließend werden die Objekte der temporären Datenbank dynamisch in das Frontend eingebunden und können gemeinsam mit den Daten des Backends verwendet werden (2).

Technischer Hintergrund

Die Informationen über die zu erstellenden Objekte sollen ihrerseits in mehreren relational verknüpften Tabellen gespeichert werden. Die darin enthaltenen Daten werden später von entsprechenden Routinen ausgelesen und in Form der temporären Datenbank umgesetzt.

Die Hierarchie aus Abb. 2 veranschaulicht die in den Tabellen zu speichernden Informationen. Dabei bildet die Datenbank das Grundgerüst und enthält die einzelnen Tabellen mit ihren Feldern.

Objekte der temporären Datenbank.tif

Abb. 2: Objekte der temporären Datenbank

Jede Tabelle kann aus Optimierungsgründen auch Indizes besitzen. Daraus leiten sich die nachfolgend beschriebenen Tabellenentwürfe ab.

tbl_sys_TempDatabases

Die Tabelle tbl_sys_TempDatabases speichert die Einstellungen für die temporäre Datenbank (s. Tab. 1).

Feldname

Beschreibung

Id

Eindeutige Kennung

DatabasePath

Pfadangabe für den Speicherort

DatabaseName

Name der anzulegenden Datenbank

KillingExistingDB

Soll eine bereits existierende Datenbank gelöscht werden?

Tab. 1: Struktur der Tabelle tbl_sys_TempDatabases

tbl_sys_TempTableDefs

Die Tabelle tbl_sys_TempTableDefs speichert Informationen über die zu erstellenden temporären Tabellen (s. Tab. 2).

Feldname

Beschreibung

Id

Eindeutige Kennung

DatabaseId

Referenzwert aus tbl_sys_TempDatabases

TableName

Name des Objektes in der temporären Datenbank

Comment

Beschreibung für das Tabellenobjekt

IsSystemObject

Wenn es sich um eine Systemtabelle handelt, wird sie ausgeblendet.

LinkWithApplication

Soll die temporäre Tabelle in das Frontend eingebunden werden?

LinkedTableName

Name der verbundenen Tabelle im Frontend

OutOfUse

Legt fest, ob dieses Objekt aktuell erzeugt werden soll.

Tab. 2: Struktur von tbl_sys_TempTableDefs

Diese Tabelle berücksichtigt, dass eine temporäre Tabelle nicht immer mit dem Beenden der Anwendung gelöscht werden muss. Sie können also für bestimmte Tabellen festlegen, dass diese nur erzeugt werden, wenn sie nicht schon vorhanden sind. Außerdem können Sie mit der Eigenschaft OutOfUse angeben, dass eine Tabelle derzeit nicht angelegt werden soll. Falls Sie diese später noch einmal benötigen, stellen Sie die Eigenschaft einfach wieder auf True ein.

tbl_sys_TempFieldDefs

Die Tabelle tbl_sys_TempFieldDefs bechreibt die in den temporären Tabellen enthaltenen Felder und deren Eigenschaften (s. Tab. 3). Die Angabe des Datentyps erfolgt über numerische Werte, die den VB-Konstanten entsprechen (s. Tab. 4).

Feldname

Beschreibung

Id

Eindeutige Kennung

DatabaseId

Referenzwert aus tbl_sys_TempDatabases

TableDefId

Referenzwert aus tbl_sys_TempTableDefs

FieldName

Name des Felds

FieldType

Datentyp der zu speichernden Werte

FieldSize

Datengröße (ist nur bei Text von Relevanz)

OrdinalPosition

Reihenfolge in der Tabelle

AutoIncrement

Handelt es sich bei dem Feld um einen Autowert?

ZeroLength

Ist es erlaubt, eine leere Zeichenfolge zu speichern?

Tab. 3: Struktur von tbl_sys_TempFieldDefs

VB-Konstante

Wert

Speichergröße

dbBoolean

1

1 Bit

dbByte

2

1 Byte

dbInteger

3

2 Byte

dbLong

4

4 Byte

dbCurrency

5

8 Byte

dbSingle

6

4 Byte

dbDouble

7

8 Byte

dbDate

8

8 Byte

dbBinary

9

<= 1 GB

dbText

10

<=255 Byte

dbMemo

12

<=65.535 Byte

dbGUID

13

16 Byte

dbDecimal

14

8 Byte

Tab. 4: Datentypen in VB(A)

tbl_sys_TempIndexDefs

Für die Optimierung bei Abfragen und/oder Suchen in Recordsets sind Indizes unabdingbar. Die in Tab. 5 beschriebene Tabelle enthält die Felder zum Speichern der Informationen über das Aussehen der Indizes.

Feldname

Beschreibung

TableDefId

Referenzwert aus tbl_sys_TempTableDefs

IndexName

Name des zu verwendenden Index

PrimaryKey

Primärschlüssel

UniqueKey

Eindeutige Werte

Clustered

Sortierter Index

Tab. 5: Struktur von tbl_sys_TempIndexDefs

tbl_sys_TempIndexFieldDefs

Ein Index kann aus einem oder mehreren Feldern bestehen. Die in Tab. 6 beschriebene Tabelle speichert die passenden Informationen.

Feldname

Beschreibung

IndexId

Referenzwert aus der Tabelle tb_sys_TempIndexDefs

FieldId

Referenzwert aus der Tabelle tbl_sys_TempFieldDefs

OrdinalPosition

Reihenfolge der Felder im Index

Tab. 6: Struktur von tbl_sys_TempIndexFieldDefs

Abb. 3 veranschaulicht die Beziehungen zwischen den oben beschriebenen Tabellen. Die Tabelle tbl_sys_TempDatabases enthält eine oder mehrere Datenbankdefinitionen. Die darin enthaltenen Tabellen liefert die Tabelle tbl_sys_TempTableDefs und ordnet die Tabellen über das Feld DatabaseID dem passenden Datensatz der Tabelle tbl_sys_TempTableDefs zu. Die beiden Tabellen tbl_sys_TempFieldDefs und tbl_sys_TempIndexDefs sind beide per 1:n-Beziehung mit der Tabelle tbl_sys_TempTableDefs verknüpft und liefern die passenden Felder und Indizes. Schließlich sorgt die Verknüpfungstabelle tbl_sys_TempIndexFieldDefs für die Zuordnung der Felder zu den einzelnen Indizes.

Tabellenbeziehungen.tif

Abb. 3: Beziehungen zwischen den Relationen

Programmierung

Die Routine zum Erstellen der temporären Datenbank können Sie in einer Klasse kapseln. Sie erstellt die temporäre Datenbank in den folgenden Schritten:

  • Erstellen des Datenbankobjektes
  • Erstellen der Tabellen mit Feldern und Indizes
  • Einbinden der Tabellen in das Frontend

Im nächsten Schritt erstellen Sie die Klasse, die für die Erstellung der Datenbank und deren Objekte zuständig ist. Sie enthält die Konstanten und Variablen aus Listing 1.

Listing 1: Konstanten und Klassenvariablen

Private Const tblDataBases As String = "tbl_sys_TempDatabases"

Private Const tblTableDefs As String = "tbl_sys_TempTableDefs"

Private Const tblFieldDefs As String = "tbl_sys_TempFieldDefs"

Private Const tblIndexDefs As String = "tbl_sys_TempIndexDefs"

Private Const tblIndexFieldDefs As String = "tbl_sys_TempIndexFieldDefs"

Private Const DATABASE As String = ";Database="

Private Const Description As String = "Description"

Private Const SQL_TableDefs As String = "SELECT * FROM tbl_sys_TempTableDefs WHERE OutOfUse = 0 AND DatabaseId = "

Private Const SQL_FieldDefs As String = "SELECT * FROM tbl_sys_TempFieldDefs"

Private Const SQL_IndexDefs As String = "SELECT * FROM tbl_sys_TempIndexDefs"

Private Const SQL_IndexFieldDefs As String = "SELECT i.IndexId, f.FieldName, i.OrdinalPosition FROM tbl_sys_TempIndexFieldDefs i INNER JOIN tbl_sys_TempFieldDefs f ON (i.FieldId = f.Id)" ' WHERE i.IndexId = "

Private temp_DBPath As String

Private clsDB As Object

Private temp_db As Object

Private temp_TDef As Object

Private temp_fld As Object

Private temp_idx As Object

Private temp_RSDB As Object

Private temp_rsTDef As Object

Private temp_rsFld As Object

Private temp_rsIdx As Object

Private temp_rsIdxFld As Object

Das Initialisieren der Klasse löst automatisch das Ereignis Class_Initialize aus. Dieses lässt sich prima einsetzen, um Recordset-Objekte mit den Informationen über die benötigten Datenbanken und deren Tabellenobjekte zu erzeugen (s. Listing 2).

Listing 2: Das Initialize-Ereignis legt Recordset-Objekte mit den grundlegenden Informationen über die zu erzeugende Datenbank an.

Private Sub Class_Initialize()

     On Error GoTo ErrorHandler

     Set clsDB = DBEngine(0)(0)

     ' Retrieving information about all temporary databases

     Set temp_RSDB = clsDB.OpenRecordset(tblDataBases, dbOpenDynaset)

     Set temp_rsTDef = clsDB.OpenRecordset(tblTableDefs, dbOpenDynaset)

ExitCode:

     Exit Sub

ErrorHandler:

     Debug.Print Err.Number, Err.Description

     Resume ExitCode

End Sub

Das Ereignis Terminate wird – ebenfalls automatisch – beim Zerstören der Klasse aufgerufen und enthält einige Anweisungen, um die verwendeten Objektvariablen wieder freizugeben:

Private Sub Class_Terminate()

     On Error Resume Next

     Set clsDB = Nothing

     Set temp_db = Nothing

     Set temp_RSDB = Nothing

     Set temp_rsTDef = Nothing

     Set temp_rsFld = Nothing

     Set temp_rsIdx = Nothing

     Set temp_rsIdxFld = Nothing

     Set temp_TDef = Nothing

     Set temp_fld = Nothing

End Sub

Methode für die Erstellung der temporären Datenbank

Innerhalb der Klasse gibt es eine zentrale Methode, die für die Erstellung der kompletten Konfiguration zuständig ist und die alle weiteren Routinen aufruft (s. Listing 3). Der vollständige Code der Klasse befindet sich in der Beispieldatenbank.

Listing 3: Diese Funktion erzeugt die temporäre Datenbank und ruft weitere Routinen zum Anlegen der übrigen Elemente auf.

Public Function CreateTempDB() As Boolean

     On Error GoTo ErrorHandler

     Dim lngErrNo As Long

     While Not temp_RSDB.EOF

         temp_DBPath = temp_RSDB!DatabasePath

         temp_DBPath = fn_app_ReplaceEnviron(temp_DBPath)

         If Dir(temp_DBPath, vbDirectory) = vbNullString Then

             Call MsgBox("Path " & temp_DBPath & " does not exist. Check the system settings!", _

                 vbExclamation)

         End If

         If Right$(temp_DBPath, 1) <> Chr$(92) Then temp_DBPath = temp_DBPath & Chr$(92)

         If temp_DBPath = ".\" Then temp_DBPath = ApplicationPath(clsDB)

         temp_DBPath = temp_DBPath & temp_RSDB!DatabaseName

         If Dir(temp_DBPath, vbNormal) <> vbNullString Then

             If temp_RSDB!KillExistingDB Then

                 On Error Resume Next

                 Call Kill(temp_DBPath)

                 lngErrNo = Err.Number

                 On Error GoTo Error-Handler

                 Select Case lngErrNo

                     Case 0: DoEvents

                         Set temp_db = DBEngine.CreateDatabase(temp_DBPath, dbLangGeneral)

                     Case 70: Set temp_db = DBEngine.OpenDatabase(temp_DBPath)

                     Case Else:

                         Call Kill(temp_DBPath)

                         Set temp_db = DBEngine.CreateDatabase(temp_DBPath, dbLangGeneral)

                 End Select

             Else

                 Set temp_db = DBEngine.OpenDatabase(temp_DBPath)

             End If

         Else

             Set temp_db = DBEngine.CreateDatabase(temp_DBPath, dbLangGeneral)

         End If

         Call CreateTempTables(temp_RSDB!Id)

         temp_RSDB.MoveNext

     Wend

     CreateTempDB = -1

ExitCode:

     On Error Resume Next

     temp_RSDB.Close

     Set temp_RSDB = Nothing

     Exit Function

ErrorHandler:

     MsgBox Err.Description, vbInformation, "TempDB:CreateTempDB"

     Resume ExitCode

End Function

Die Funktion CreateTempDB durchläuft das bereits geöffnete Recordset tmp_RSDB mit allen zu erstellenden Datenbanken und führt für jeden einzelnen Datensatz weitere Aktionen aus.

Zunächst entfernt sie mit der Hilfsfunktion fn_app_ReplaceEnviron alle Umgebungsvariablen aus dem angegebenen Datenbankpfad. Diese Funktion überprüft den Datenpfadstring auf das Vorkommen besonderer Variablen, die durch die VB(A)-Funktion ENVIRON ersetzt werden.

Anschließend überprüft die Routine, ob der angegebene Pfad existiert. Sollte der Pfad nicht existieren, gibt sie eine Fehlermeldung aus und versucht, die nächste Datenbank anzulegen.

Sollte die Datenbank bereits existieren, prüft die Routine anhand des Parameters KillingExistingDB, ob diese gelöscht werden soll, und verwendet gegebenenfalls den VB(A)-Befehl KILL zum Löschen der Datenbank.

Leider funktioniert das Löschen einer Datenbank nicht immer so einfach, wie man es gerne möchte. Sollte bereits auf eine darin enthaltene verknüpfte Tabelle zugegriffen worden sein, wird die Datenbank mittels LDB-Datei gesperrt und das Löschen der Datenbank verhindert sowie eine Fehlermeldung angezeigt. Sollte das Löschen erfolgreich gewesen sein, so wird eine neue Datenbank erstellt:

Set temp_db = DBEngine.CreateDatabase(DatenbankName, dbLangGeneral, dbVersion40)

Nachdem die Routine mit der Datenbank das Grundgerüst erstellt hat, folgen nun die einzelnen Tabellen innerhalb der Datenbank. Dabei kommt die folgende Hilfsfunktion zum Zuge, deren Code wir aus Platzgründen hier nicht abdrucken können:

Call CreateTempTables(temp_RSDB!Id)

Diese Funktion löscht zunächst alle möglichen Tabellen, um sie anschließend neu zu erstellen. Das Füllen der Tabellen mit Feldern und Indizes übernehmen zwei weitere Funktionen:

Call CreateTableFields(tdef, !Id)

Call CreateTableIndex(tdef, !Id)

Call temp_db.TableDefs.Append(tdef)

Nach dem Erzeugen der Tabellen müssen diese noch mit dem Frontend verbunden werden. Dazu erzeugt die Routine in der lokalen Datenbank mittels CreateTableDef ein Tabellenobjekt, das auf dem zuvor erstellten temporären Objekt aus der temporären Datenbank basiert (eine Zeile):

Set ldef = clsDB.CreateTableDef(Nz(
temp_rsTDef!LinkedTableName,
temp_rsTDef!TableName), dbAttachExclusive, Nz(temp_rsTDef!TableName, vbNullString))

Besonders ist noch zu beachten, dass die Beschreibung für die Tabelle, wie sie in der Konfigurationstabelle für Tabellen vorgesehen ist, nicht einfach an das bestehende Tabellenobjekt „angehängt“ werden kann. Das hängt damit zusammen, dass ein Tabellenobjekt bei der Erstellung keine Eigenschaft namens Description kennt. Aus diesem Grund muss diese Eigenschaft für das Objekt zunächst erstellt werden:

If Not IsNull(temp_rsTDef!Comment) Then

     Set lprp = .CreateProperty

     If Not lprp Is Nothing Then

         With lprp

             .Name = Description

             .Type = dbText

             .Value = temp_rsTDef!Comment

         End With

         .Properties.Append lprp

     End If

End If

Nachdem eine mögliche Beschreibung erstellt wurde, wird mit der nächsten Tabelle fortgefahren.

Konfigurator für temporäre Datenbanken

In der Beispieldatenbank befindet sich ein Konfigurator, den Sie mit allen oben beschriebenen Tabellen und der Klasse in Ihr Frontend importieren. Starten Sie anschließend den Konfigurator, um die temporären Tabellen für Ihre Anwendung zu konfigurieren und zu testen.

Nachfolgend finden Sie eine Beschreibung des Konfigurators sowie seiner Funktionen. Der Code der Klasse sowie des Konfigurators ist offen und kann jederzeit von Ihnen verwendet werden.

Sie starten den Konfigurator, indem Sie das Formular frm_app_Konfigurator öffnen (s. Abb. 4).

Konfigurator_#1.tif

Abb. 4: Erste Schritte im Konfigurator

Zunächst definieren Sie den Datenbankpfad sowie den Namen der zu verwendenden Datenbank. Hierbei können Sie auch Umgebungsvariablen oder relative Pfade für die Definition des Zielortes festlegen.

Die Funktion fn_app_ReplaceEnviron aus der Klasse TempDB können Sie beliebig Ihren Bedürfnissen anpassen (s. Listing 4).

Listing 4: Diese Funktion ersetzt Platzhalter für Umgebungsvariablen durch die tatsächlichen Werte.

Private Function fn_app_ReplaceEnviron(DatabasePath As String) As String

     On Error Resume Next

     Dim tmpString As String

     tmpString = DatabasePath

     tmpString = Replace(tmpString, "%TEMP%", Environ("Temp"))

     tmpString = Replace(tmpString, "%TMP%", Environ("TMP"))

     tmpString = Replace(tmpString, "%SystemDrive%", Environ("SystemDrive"))

     tmpString = Replace(tmpString, "%Systemroot%", Environ("Systemroot"))

     tmpString = Replace(tmpString, "%Homedrive%", Environ("Homedrive"))

     tmpString = Replace(tmpString, "%Homepath%", Environ("Homepath"))

     tmpString = Replace(tmpString, "%Homeshare%", Environ("Homeshare"))

     tmpString = Replace(tmpString, "%ALLUSERSPROFILE%", Environ("ALLUSERSPROFILE"))

     fn_app_ReplaceEnviron = tmpString

End Function

Nachdem Sie Pfad und Namen der Datenbank festgelegt haben, geben Sie auf der Registerseite „Tabellen“ die zu verwendenden Tabellen an:

  • Tabellenname: Bestimmt den Namen der Tabelle, wie er in der temporären Datenbank angelegt werden soll.
  • Beschreibung: Beschreibung der Tabelle, die im Datenbankfenster von Access angezeigt wird.
  • sys: Handelt es sich um eine Systemtabelle, wird sie gewöhnlich nicht angezeigt.
  • Link: Soll die angelegte Tabelle mit dem Frontend verbunden werden? Sehr häufig kommt es vor, dass Tabellen nur für die Speicherung von Zwischenergebnissen verwendet werden.
  • LinkName: Name der Tabelle, wie er im Frontend verwendet werden soll.
  • Deaktiviert: Wenn Sie die Tabelle nicht benötigen, sollten Sie sie nicht löschen, sondern lediglich deaktivieren. Dann können Sie sie später immer wieder verwenden.

Attribute der Relationen bestimmen

Nachdem Sie die Informationen über die Tabellen angelegt haben, müssen Sie im nächsten Schritt die Struktur der einzelnen Tabellen festlegen.

Dazu öffnen Sie die Registerseite Felder wie in Abb. 5 dargestellt. Hier können Sie die Namen und Datentypen der zu verwendenden Felder festlegen.

Konfigurator_#2.tif

Abb. 5: Festlegen der Felder für die Tabelle

Wählen Sie zunächst aus dem Listenfeld Tabellenname die zu konfigurierende Tabelle aus.

Legen Sie dann neben Name und Datentyp auch fest, wie groß die zu speichernde Datenmenge ist und/oder ob Sie einen Autowert verwenden möchten.

Indizes für die Tabellen

Grundsätzlich ist die Arbeit mit dem Anlegen der Tabellen und Felder erledigt.

Jedoch speichern Sie unter Umständen so große Datenmengen, dass die Verwendung von Indizes sinnvoll erscheint.

Deshalb können Sie auf einer weiteren Registerseite für jede Tabelle beliebige Indizes festlegen (s. Abb. 6).

Konfigurator_#3.tif

Abb. 6: Indizes beschleunigen die Suche nach Daten

Wählen Sie auch hier zunächst die Tabelle aus und legen Sie anschließend die Namen und die Eigenschaften für die zu erstellenden Indizes fest.

Erstellen der temporären Datenbank

Geschafft – alle Tabellen, Felder und Indizes sind konfiguriert. Nun erfolgt der Testlauf! Klicken Sie auf die Schaltfläche Datenbank erstellen und schon können Sie testen, ob die von Ihnen angelegte Konfiguration funktioniert.

Sollte alles fehlerfrei durchlaufen, so finden Sie als Ergebnis im Tabellenfenster Ihrer Anwendung weitere Tabellen vor, die aus der zuvor erstellten temporären Datenbank kommen (s. Abb. 7).

Konfigurator_#4.tif

Abb. 7: Ergebnis nach dem Erstellen der Tabellen

Fazit und Ausblick

Mit Hilfe von temporären Tabellen können Sie statische Daten sowie aufzubereitende Daten aus dem Backend Ihrer Anwendung auf den Client verlagern. Dadurch vermeiden Sie gerade in einem Netzwerk unnötigen Netzverkehr und beschleunigen außerdem noch Ihre Anwendung. Um dieses Werkzeug in Ihrer Anwendung einzusetzen, kopieren Sie alle Tabellen, Formulare und Module einfach in Ihre Anwendung. Konfigurieren Sie anschließend Ihre Tabellen und testen Sie sie. Sollte alles reibungslos funktionieren, benötigen Sie nur noch einen Aufruf für die Erstellung der Datenbank, der beim Öffnen Ihrer Anwendung ausgeführt wird, und schon steht einer komfortablen Benutzung der Tabellen nichts mehr im Wege.

Kompletten Artikel lesen?

Einfach für den Newsletter anmelden, dann lesen Sie schon in einer Minute den kompletten Artikel und erhalten die Beispieldatenbanken.

E-Mail:

Download

Download

Die .zip-Datei enthält folgende Dateien:

TemporaryDatabases.mdb

Beispieldateien downloaden

© 2003-2015 André Minhorst Alle Rechte vorbehalten.