Access: Massendaten zum Testen erzeugen

01.01.2007 von Lorenz  Hölscher
So ziemlich für jede Datenbank benötigen Sie während der Entwicklung Beispieldaten zum Testen. Damit irgendetwas vorhanden ist, tippen Sie schnell „asdf“ für Texte, „4711“ für Zahlenfelder und „1.1.01“ für ein Datum. Das wird noch dreimal kopiert und muss dann reichen. Warum so geizig? Testdaten lassen sich mit wenig Aufwand jederzeit in beliebiger Menge beschaffen, so dass auch von Anfang an Performancemessungen möglich sind.

Schreiben Sie eine Prozedur wie in Listing 1, welche in eine Tabelle eine beliebige Menge Daten hineinschreibt. Für dieses Beispiel benötigen Sie eine Tabelle tblMengen mit den Feldern wie in Tabelle 1.

Tabelle 1: Benötigte Felder

Feldname

Typ

Größe

Mtext

Text

255

Mzahl

Zahl

Double

Mdatum

Datum

Dann können Sie mittels Zufallsfunktion und einer Schleife viele zufallsgesteuerte Datensätze erzeugen. Listing 1 ermittelt mit der Rnd-Funktion einen Wert zwischen 0 und 1, der mit der Differenz zwischen jeweiligem Minimum und Maximum multipliziert wird. Falls beispielsweise das Minimum 3 und das Maximum 10 war, ergeben sich Werte zwischen 0 und 7 inklusive. Zu diesem Wert muss dann das Minimum wieder addiert werden. Diese Formel gilt sowohl für eine Double-Zahl als auch für das Datum. Wenn Sie ganzzahlige Ergebnisse oder ein Datum ohne Uhrzeit benötigen, entfernt die Funktion CLng (ConvertToLong) die jeweiligen Nachkommastellen. Diese Prozedur erzeugt schon die gewünschten Zahlen und Datumswerte, nur noch keine Texte.

Listing 1 - Massendaten mit Datum und Zeit

Option Explicit
Const AnzahlZeilen = 10
Const minZahl = 0
Const maxZahl = 100
Const minDat = #1/1/1960#
Const maxDat = #1/1/2007#
Sub ErzeugeDaten()
Dim RS As Recordset
Dim dblDatum As Double
Dim dblZahl As Double
Dim lngZaehler As Long

'Zufallsgenerator initialisieren
Randomize Now()
'DAO-Schreib-Zugriff auf Tabelle
Set RS = CurrentDb.OpenRecordset( "tblMengen", dbOpenDynaset)
For lngZaehler = 1 To AnzahlZeilen
dblDatum = ((maxDat - minDat) * Rnd) + minDat
dblZahl = ((maxZahl - minZahl) * Rnd) + minZahl

'neuen Datensatz einfügen
RS.AddNew
RS.Fields("MDatum").Value = dblDatum
RS.Fields("MZahl").Value = dblZahl

' oder ganzzahlig bzw. ohne Uhrzeit
' RS.Fields("MDatum").Value = CLng(dblDatum)
' RS.Fields("MZahl").Value = CLng(dblZahl)

RS.Update
Next
End Sub

Besonderheiten bei Texten

Bei Texten muss pro Datensatz nicht nur ein einziger Wert, nämlich ein Buchstabe, sondern eine unterschiedlich lange Reihe von Werten erzeugt werden. Diese Reihe soll im Folgenden der Einfachheit halber „Satz“ genannt werden, auch wenn sie sinnfrei ist.

Sätze erzeugen

In Listing 2 sehen Sie die entsprechende Technik, um innerhalb der For-Next-Schleife über alle Datensätze mittels einer zweiten For-Next-Schleife mehrere Zeichen für diesen Satz zu ermitteln.

Der jeweils nächste Buchstabe wird von der Funktion HolDasZeichen zurückgegeben. Diese funktioniert wie die Ermittlung von dblZahl oder dblDatum aus Listing 1: Die Differenz zwischen Minimum und Maximum mit der Zufallszahl multiplizieren und den Minimalwert wieder hinzuaddieren. Der Sonderfall besteht hier nur darin, dass die Buchstaben anhand ihrer internen Nummern berechnet werden. Die Funktion heißt immer noch Asc, weil zu DOS-Zeiten das ASCII-Schema üblich war. Sie liefert Ihnen inzwischen je nach Betriebssystem die ANSII- oder Unicode-Nummer zurück. Für den hier benutzten Bereich der Groß- und Kleinbuchstaben unterscheiden sich die Nummern nicht. Mit der Funktion Asc wird die Nummer des Buchstabens, also 65 für ein "A", ermittelt, dann wie bei Zahlen per Zufallsfunktion umgerechnet und anschließend mit Chr(<Nummer>) wiederin den zugehörigen Buchstaben zurückgerechnet.

Listing 2 - Einfachen Text erzeugen

Option Explicit
Const AnzahlZeilen = 10
Const maxZchnGesamt = 50

Sub ErzeugeDaten()
Dim RS As Recordset
Dim strText As String
Dim lngZaehler As Long
Dim intAnz As Integer

'DAO-Schreib-Zugriff auf Tabelle
Set RS = CurrentDb.OpenRecordset( "tblMengen", dbOpenDynaset)
For lngZaehler = 1 To AnzahlZeilen
strText = vbNullString
For intAnz = 1 To Int(maxZchnGesamt * Rnd)
strText = strText & HolDasZeichen()
Next
'neuen Datensatz einfügen
RS.AddNew
RS.Fields("mText").Value = strText
RS.Update
Next
End Sub

Function HolDasZeichen() As String
HolDasZeichen = ((Asc("A") - Asc("z") - 1) * Rnd) + Asc("z") + 1
HolDasZeichen = Chr(HolDasZeichen)
End Function

Daten optimieren

Leider liegen zwischen den Großbuchstaben, nämlich A=65 bis Z=90, und den Kleinbuchstaben, nämlich a=97 bis z=122, noch einige Sonderzeichen wie eckige Klammern oder Unterstrich. Daher enthalten die Ergebnisse kryptische Wörter wie „fQRp_pcy“, „pvbC\^Cnedk^s]C[FUaSwOR^“ oder „fY“.

Damit es wenigstens so ähnlich wie echte Eingaben aussieht, sollen regelmäßig verteilte Leerzeichen vorkommen sowie Minimal- und Maximal-Wortlängen eingehalten werden. Außerdem sollen die Zeichen auf reine Buchstaben beschränkt werden, damit die Sonderzeichen zwischen Z und a entfallen. Zusätzlich müssen aber die in der ANSI-Nummerierung außerhalb liegenden Umlaute und das Leerzeichen erlaubt werden.

Die Funktion HolDasZeichen muss wie in Listing 3 verändert werden, damit in einer Endlos-Schleife so lange ein neues Zeichen per Zufall gesucht wird, bis die Funktion IstZeichenOK es als brauchbar akzeptiert. In IstZeichenOK sehen Sie, dass es sogar möglich ist, die Zeichen direkt mit einem Größer- oder Kleiner-Operator zu vergleichen. Das funktioniert, weil intern tatsächlich die ANSI-Nummern verglichen werden. Da Leerzeichen und Umlaute mühsam einzeln zu prüfen sind, lässt es sich am bequemsten mit InString erledigen, da in strZchn ja immer nur ein Zeichen steht.

Function HolDasZeichen() As String
Do
HolDasZeichen = ((Asc("A") - Asc("z") - 1) * Rnd) + Asc("z") + 1
HolDasZeichen = Chr(HolDasZeichen)
Loop Until IstZchnOK(HolDasZeichen)
End Function

Function IstZchnOK(strZchn As String) As Boolean
IstZchnOK = InStr("ÄÖÜ äöüß", strZchn)
IstZchnOK = IstZchnOK Or (strZchn >= "A" And strZchn <= "Z")
IstZchnOK = IstZchnOK Or (strZchn >= "a" And strZchn <= "z")
End Function

Leerzeichen und Wörter

Während die ursprüngliche Funktion ErzeugeMengen dank Listing 3 nun bereits nur Buchstaben erzeugt, sollen diese nun regelmäßig durch Leerzeichen unterbrochen werden. Dazu wird anhand der Konstanten minZchnJeWort und maxZchnJeWort die typische Länge eines Wortes festgelegt.

Die For intAnzZchn-Schleife in Listing 4 hat durch die Zufallsfunktion unterschiedliche Längen für die Gesamtlänge des Satzes. In intLetztesLeer wird jeweils die Position desletzten Leerzeichens gespeichert. Wenn der vorige Buchstabe ein Leerzeichen war, darf der aktuell mit HolDasZeichen ermittelte auch ein Großbuchstabe sein. Jede andere Position wäre nicht am Wort- oder Satzanfang und muss daher ein Kleinbuchstabe werden. Ob ein Leerzeichen angehängt wird, ist von zwei Faktoren abhängig:

Listing 4 - Wörter mit Großbuchstaben

Option Explicit
Const AnzahlZeilen = 10
Const maxZchnGesamt = 50
Const minZchnJeWort = 3
Const maxZchnJeWort = 20

Sub ErzeugeMengen()
Dim RS As Recordset
Dim strText As String
Dim lngZaehler As Long
Dim intWortLang As Integer
Dim intZufallLeer As Integer
Dim intAnzZchn As Integer
Dim intLetztesLeer As Integer

'Zufallsgenerator initialisieren
Randomize Now()
'DAO-Schreib-Zugriff auf Tabelle
Set RS = CurrentDb.OpenRecordset("tblMengen", dbOpenDynaset)
For lngZaehler = 1 To AnzahlZeilen
strText = vbNullString
intLetztesLeer = 0
For intAnzZchn = 1 To Int(maxZchnGesamt * Rnd) + 1
'am Anfang groß und klein möglich
If intLetztesLeer = intAnzZchn - 1 Then
'also ist hiervor ein Leerzeichen
strText = strText & HolDasZeichen()
Else
'jeder andere Buchstabe immer klein
strText = strText & LCase(HolDasZeichen())
End If
'aktuelle Wortlänge ermitteln
intWortLang = (intAnzZchn - intLetztesLeer)
'Zufallswert für Wortlänge ermitteln
intZufallLeer = Int((maxZchnJeWort - minZchnJeWort) * Rnd + minZchnJeWort)
'wenn Wort-Maximallänge überschritten oder
'die Wortlänge dem Zufallswert entspricht...
If intZufallLeer = intWortLang Or intWortLang >= maxZchnJeWort Then
'... Leerzeichen an "Wort" anhängen
strText = strText & " "
'diese Leerzeichen-Position merken
intLetztesLeer = intAnzZchn
End If
Next
'neuen Datensatz einfügen
RS.AddNew
RS.Fields("mText").Value = strText
RS.Update
Next
End Sub

Satzlänge ist nicht exakt

Wenn Sie den Code ganz genau analysieren, werden Sie feststellen, dass die maximal vorgegebene Länge des Satzes überschritten wird. Wenn die Schleife beispielsweise zufällig von 1 bis 20 liefe, aber zwischendurch fünf Leerzeichen eingefügt werden, ist der Satz tatsächlich 25 Zeichen lang. Es wäre möglich, nach dem Einfügen eines Leerzeichens den entsprechenden Gesamtzähler zu korrigieren. Das hätte den Code hier aber unübersichtlicher gemacht.

Gesamtmakro

Natürlich sollen nicht wie bisher nur abwechselnd entweder Texte oder Zahlen/Datum erzeugt werden können. Beide Makros lassen sich in einem zusammenfassen wie in Listing 5.Das Listing 5 enthält keine Kommentare mehr, da die Funktionen identisch mit den Teilmakros sind. Es wurde jedoch erweitert um die Sanduhr und eine in der Statuszeile angezeigte Fortschrittsmeldung. Schließlich ging es ja darum, besonders viele Daten erzeugen zu können, was ein wenig dauern kann. (loh/mha)

Lorenz Hölscher <loh> bietet seit über 10 Jahren Trainings für alle Microsoft Office-Produkte und (VBA-)Programmierung an. Seine Schwerpunkte sind Access, Word und Excel sowie begleitende grafische Arbeiten. Seine „branchenfremden“ Fachkenntnisse als Architekt, Layouter und Designer kann er gut in die jetzige Arbeit integrieren. Sie erreichen ihn unter L.Hoelscher@cls-software.de.

Listing 5 - Gesamtmakro

Option Compare Database
Option Explicit
Const AnzahlZeilen = 10
Const minZahl = 0
Const maxZahl = 100
Const minDat = #1/1/1960#
Const maxDat = #1/1/2005#
Const maxZchnGesamt = 50
Const minZchnJeWort = 3
Const maxZchnJeWort = 20

Sub ErzeugeMengen()
Dim RS As Recordset
Dim dblDatum As Double
Dim dblZahl As Double
Dim strText As String
Dim lngZaehler As Long
Dim intWortLang As Integer
Dim intZufallLeer As Integer
Dim intAnzZchn As Integer
Dim intLetztesLeer As Integer
DoCmd.Hourglass True
Randomize Now()
Set RS = CurrentDb.OpenRecordset( "tblMengen", dbOpenDynaset)
For lngZaehler = 1 To AnzahlZeilen
'in Statusleiste Fortschritt melden
DoCmd.Echo True, "Erzeuge Datensatz " & lngZaehler
dblDatum = ((maxDat - minDat) * Rnd) + minDat
dblZahl = ((maxZahl - minZahl) * Rnd) + minZahl
strText = vbNullString
intLetztesLeer = 0
For intAnzZchn = 1 To Int(maxZchnGesamt * Rnd) + 1
If intLetztesLeer = intAnzZchn - 1 Then
strText = strText & HolDasZeichen()
Else
strText = strText & LCase(HolDasZeichen())
End If
intWortLang = (intAnzZchn - intLetztesLeer)
intZufallLeer = Int((maxZchnJeWort - minZchnJeWort) * Rnd + minZchnJeWort)
If intZufallLeer = intWortLang Or intWortLang >= maxZchnJeWort Then
strText = strText & " "
intLetztesLeer = intAnzZchn
End If
Next
RS.AddNew
RS.Fields("MDatum").Value = dblDatum
RS.Fields("MZahl").Value = dblZahl
RS.Fields("mText").Value = strText
RS.Update
Next
DoCmd.Hourglass False
End Sub

Was bedeuten ASCII und ANSI?

ASCII ist die Abkürzung für American Standard Code for Information Interchange. Es ist ein Standardverfahren für die Codierung von Buchstaben, Sonder- und Steuerzeichen mit 7 Bits. Damit sind 2^7=128 Zeichen speicherbar.

Da es amerikanischen Ursprungs ist, fehlten zu Beginn deutsche Sonderzeichen für Umlaute oder das scharfe „ß“. In einer „German ASCII“ genannten, deutschen Version wurden sie anstelle wenig gebräuchlicher Zeichen mit aufgenommen. Seit der internationalen Verbreitung von Computern hat sich die Erkenntnis durchgesetzt, dass nicht jede Sprache ihre eigene Codierung benutzen kann, da sonst beispielsweise E-Mails verstümmelt oder ganz unleserlich würden.

Mit ANSI hat das American National Standards Institute eine 8-Bit-Codierung definiert, die also 2^8=256 Zeichen erlaubt, so dass mehr Reserve für Sonderzeichen vorhanden ist. Das ist jedoch selbst für die Sonderzeichen westlicher Länder immer noch zu wenig (etwa für Tschechisch), von Arabisch, Japanisch, Indonesisch oder Chinesisch ganz zu schweigen. Daher ist derzeit die Unicode-Schriftspeicherung mit 2^16=65.000 Zeichen pro Schrift aktuell. Die jeweilige Schrifttabelle lässt sich mit dem Windows-Hilfsprogramm „Zeichen-Tabelle“ ansehen.