Access [basics] Programmieren mit Arrays

Transcription

Access [basics] Programmieren mit Arrays
Access [basics]
VBA-Grundlagen
Programmieren mit Arrays
Programmieren mit Arrays
Dass Sie unter Access Daten in Tabellen speichern und gezielt darauf zugreifen können, wissen Sie als Access [basics]-Leser schon längst. Aber was, wenn Sie nur ein paar gleichartige Daten zwischenspeichern
und wieder darauf zugreifen möchten? Legen Sie dann im VBA-Code für jedes Element eine eigene Variable
an? Nein! Hier kommen zum Beispiel die Arrays, auch Datenfelder genannt, zum Einsatz. Der vorliegende
Artikel zeigt, wie Sie Array erstellen, mit Daten füllen und die Daten wieder auslesen.
Beispieldatenbank
Die Beispiele dieses Artikels finden Sie in der Datenbank 1301_Arrays.mdb.
Arrays
Array sind prinzipiell eine Art Tabelle, wobei Sie nicht
über Datensätze und Feldnamen auf den in einem
Elemend des Array gespeicherten Wert zugreifen,
sondern über Index-Werte.
Zusätzlich kann ein Array eine, zwei oder auch mehr
Dimensionen enthalten und diese mit Elementen füllen.
Ein Array mit einer Dimension würde beispielsweise
einer Tabelle mit einem einzigen Feld entsprechen
und ein Array mit zwei Dimensionen einer Tabelle
mit einem bis n Feldern. Arrays mit mehr als zwei
Dimensionen lassen sich schlecht auf Tabellen übertragen.
Eindimensionale Arrays
Mit einem eindimensionalen Array lassen sich einfache Wertereihen speichern – beispielsweise die
Monatsnamen. Um die Vereinfachung durch den Einsatz von Array zu verdeutlichen, schauen wir uns zunächst an, wie Sie Monatsnamen auf umständliche
Weise in Variablen speichern können. Dazu legen Sie
in einem Modul namens mdlArrays (VBA-Editor mit
Alt + F11 ) öffnen, dann Einfügen|Modul auswählen)
zunächst die öffentlich deklarierten String-Variablen
an (dieses und die folgenden Beispiele finden Sie im
Modul mdlArrays):
Public
Public
Public
...
Public
strMonat01 As String
strMonat02 As String
strMonat03 As String
strMonat12 As String
In einer Prozedur namens MonateInVariablen füllen
Sie diese mit den entsprechenden Werten:
1/2013
Seite 2
Public Sub MonateInVariablen()
strMonat01 = "Januar"
strMonat02 = "Februar"
strMonat03 = "März"
...
strMonat12 = "Dezember"
End Sub
Führen Sie diese Prozedur aus, indem Sie die Einfügemarke in der Prozedur platzieren und auf F5
drücken. Danach können Sie die Werte der einzelnen
Variablen etwa im Direktfenster (Strg + G) wie folgt
abfragen:
Debug.Print strMonat03
März
Wenn Sie per VBA auf eine der Variablen zugreifen
möchten, um den Namen des Monats zu ermitteln,
müssen Sie immer den genauen Variablen verwenden. Es gibt keine Möglichkeit, die zwölf Variablen
beispielsweise innerhalb einer Schleife schnell auszugeben.
Dies ist ein tolles Betätigungsfeld für Array. Ein Array,
das eine bestimmte Menge Daten gleichen Datentyps
aufnehmen soll (dies ist eine Einschränkung, die Sie
beachten müssen), deklarieren Sie fast genauso wie
eine normale Variable dieses Datentyps. Der einzige
Unterschied ist, dass Sie den größten zu erwartenden Wert für den Index hinter dem Variablennamen
in Klammern angeben:
Public strMonate(11) As String
Im Gegensatz zu den weiter unten besprochenen
dynamischen Arrays handelt es sich hierbei um ein
statisches Array.
Sicher werden Sie sich fragen, warum hier die Zahl
11 und nicht die der Anzahl der Monate entsprechende Zahl 12 angegeben wird. Der Grund ist, dass die
Indexwerte standardmäßig immer mit 0 beginnen.
Das bedeutet, das die Variable strMonate Werte für
die Indexwerte 0 bis 11 aufnehmen kann. Der erste
www.access-basics.de
Access [basics]
VBA-Grundlagen
Programmieren mit Arrays
Eintrag des Arrays strMonate wird also mit dem Indexwert 0, der zweite mit 1 und
so weiter festgelegt:
Public Sub MonateInArray()
strMonate(0) = "Januar"
strMonate(1) = "Februar"
strMonate(2) = "März"
...
strMonate(11) = "Dezember"
End Sub
Dementsprechend
liefert
etwa die Abfrage des Wertes Bild 1: Versuch, ein Array mit einem zu großen Indexwert zu füllen
mit dem Index 3 den vierten
Eintrag des Arrays, hier April:
sondern 1 bis 12 verwenden? Dann tritt der Fehler
aus Bild 1 auf.
Debug.Print strMonate(3)
April
Nun scheint es unkomfortabel zu sein, nicht die dem
Monat entsprechende Zahl für den Zugriff auf das
Array zu verwenden. Dies lässt sich jedoch leicht
ändern. Wenn Sie nur einen Wert für die Anzahl der
Elemente angeben, geht VBA davon aus, dass der Index 0-basiert verwaltet wird. Sie können den Wertebereich für den Indexbereich jedoch detailliert festlegen. In diesem Fall möchten wir gern die Indexwerte
1 bis 12 verwenden. Dazu deklarieren Sie die ArrayVariable wie folgt:
Das Array wurde nur für Indexwerte bis maximal 11
deklariert, also ist der Indexwert 12 nicht zulässig.
Das gilt andersherum genauso – wenn Sie also ein
Array für die Indexwerte 1 bis 12 deklarieren und das
Element mit dem Indexwert 0 füllen wollen, lösen Sie
ebenfalls einen Fehler aus.
Alle Werte auslesen
Kommen wir gleich zu einem wesentlichen Vorteil
von Arrays gegenüber einzelnen, durchnummerierten Variablen: Sie können diese mit wenigen Zeilen
per Code durchlaufen.
Public strMonate(1 To 12) As String
Die Monatsnamen weisen Sie dementsprechend nun
mit den Indexwerten 1 bis 12 zu:
Wenn Sie wissen, welche für welche Indexwerte Sie
die Elementinhalte ausgeben möchten, benötigen
Sie nur die folgende Prozedur dazu:
Public Sub MonateInArray()
strMonate(1) = "Januar"
strMonate(2) = "Februar"
strMonate(3) = "März"
...
strMonate(12) = "Dezember"
End Sub
Public Sub MonateDurchlaufen()
Dim i As Integer
Call MonateInArray
For i = 1 To 12
Debug.Print strMonate(i)
Next i
End Sub
Somit liefert das Array nun auch bei der Ausgabe die
gewünschten Werte:
Die Prozedur deklariert eine Laufvariable namens
i und füllt das Array strMonate() mit der oben erläuterten Prozedur MonateInArray. Danach durchläuft es eine Schleife und füllt dabei die Laufvariable i nacheinander mit den Werten von 1 bis 12.
Die einzige Anweisung innerhalb der Schleife gibt
den jeweiligen Wert des Arrays strMonate() für den
Index i aus.
Debug.Print strMonate(3)
März
Was aber geschieht, wenn Sie das Array nullbasiert
deklarieren, aber dann nicht die Indexwerte 0 bis 11,
1/2013
Seite 3
www.access-basics.de
Access [basics]
VBA-Grundlagen
Programmieren mit Arrays
Eindimensionale Arrays mit For Each
durchlaufen
möchten. Für diesen Fall hält VBA die Möglichkeit
der dynamischen Erweiterung bereit.
Bei eindimensionalen Arrays können Sie neben der
For...Next-Schleife auch die For Each-Schleife zum
Durchlaufen des Arrays einsetzen. Sie müssen nur
eine Variant-Variable deklarieren, die mit dem aktuellen Wert des Arrays gefüllt wird:
Im folgenden Beispiel soll ein Array namens strFirmen mit den Firmennamen aus der Tabelle tblKunden der Beispieldatenbank gefüllt werden. Um später
per Direktfenster auf das Array zugreifen zu können,
deklarieren wir es wie folgt im Kopf des Moduls mdlDynamischeArrays:
Public Sub MonateDurchlaufenForEach()
Dim varmonat As Variant
Call MonateInArray
For Each varmonat In strMonate
Debug.Print varmonat
Next varmonat
End Sub
Dim strFirmen() As String
Wie Sie sehen, haben wir hier nicht festgelegt, in welchen Grenzen sich die Indexwerte befinden dürfen.
Danach führen Sie die folgende Prozedur aus:
Der Vorteil ist, dass Sie sich hier um die Werte für die
Indexgrenzen keine Sorgen machen müssen.
Standard für die Indexbasis anpassen
Wenn Sie feststellen, dass Sie grundsätzlich mit Arrays arbeiten, deren Index mit 1 beginnt (oder auch
einer völlig anderen Zahl), können Sie die Basis des
Indexes für Arrays modulweit auf einen anderen Wert
einstellen (Beispiele im Modul mdlArraysBase1).
Für den Wert 1 legen Sie im Kopf des Moduls die folgende Anweisung an:
Diese Prozedur deklariert und füllt zwei Variablen für
das aktuelle Database-Objekt und für ein Recordset
mit allen Datensätzen der Tabelle tblKunden.
Option Base 1
Wenn Sie nun das Array für die Indexwerte von 1 bis
12 deklarieren, brauchen Sie wiederum nur die Zahl
12 in Klammern anzugeben:
Public strMonate(12) As String
Um zu prüfen, ob der Index von strMonate wirklich
bei 1 beginnt und nicht doch bei 0, fügen Sie die folgende Anweisung innerhalb der Prozedur MonateDurchlaufe ein:
strMonate(0) = "test"
Dies liefert wieder den bereits oben beschriebenen
Fehler.
Ein Array dynamisch erweitern
Nicht immer wissen Sie bereits beim Deklarieren
eines Arrays, wieviele Werte Sie darin speichern
1/2013
Public Sub ArrayAusRecordset()
Dim db As DAO.Database
Dim rst As DAO.Recordset
Set db = CurrentDb
Set rst = db.OpenRecordset("SELECT Firma " _
& "FROM tblKunden", dbOpenDynaset)
Do While Not rst.EOF
ReDim Preserve strFirmen(rst.AbsolutePosition)
strFirmen(rst.AbsolutePosition) = rst!Firma
rst.MoveNext
Loop
End Sub
Seite 4
Eine Do While-Schleife durchläuft alle Datensätze
der des Recordsets. Dabei geschehen zwei Dinge:
Als Erstes stellt die ReDim-Anweisung den größten
Wert für die erste Dimension des Arrays strFirmen
auf den Wert der Eigenschaft AbsolutePosition des
aktuelle Datensatzes des Recordsets ein. AbsolutePosition liefert die Position des Datensatzzeigers von
0 bis zur Anzahl der Datensätze minus eins.
Dadurch wird das Array mit jedem Durchlauf um ein
Element erweitert. Das Schlüsselwort Preserve ist
unbedingt zu verwenden: Es sorgt dafür, dass das
Array bei der Änderung der Elementanzahl nicht geleert wird.
Die zweite Anweisung füllt das Element mit dem
durch rst.AbsolutePosition gelieferten Index mit
dem Wert des Feldes Firma des aktuellen Datensatzes. Auf diese Weise füllt die Prozedur das Array in
www.access-basics.de
Access [basics]
VBA-Grundlagen
Programmieren mit Arrays
diesem Beispiel mit allen 91 Firmennamen der Tabelle tblKunden. Das letzte Element rufen Sie nach
dem Ausführen der Prozedur ArrayAusRecordset
beispielsweise mit folgender Anweisung im Direktfenster ab:
? strFirmen(90)
Wolski Zajazd
Indexgrenzen dynamischer Arrays
ermitteln
Arrays als Parameter übergeben
Angenommen, Sie haben wie oben angegeben ein
Array Element für Element dynamisch erweitert und
gefüllt. Wie wollen Sie dieses dann in einer Schleife
durchlaufen, ohne die Indexgrenzen genau zu kennen?
Genau für diesen Zweck gibt es zwei VBA-Funktionen. Die erste heißt LBound und liefert den Wert des
untersten Indexes, die zweite heißt UBound und liefert den oberen Wert. L steht dabei für Lower, U für
Upper.
Das folgende Beispiel zeigt den praktischen Einsatz
dieser beiden Funktionen:
Public Sub DynamischesArrayDurchlaufen()
Dim i As Integer
Dim intUntereGrenze As Integer
Dim intObereGrenze As Integer
Call ArrayAusRecordset
intUntereGrenze = LBound(strFirmen())
intObereGrenze = UBound(strFirmen())
For i = intUntereGrenze To intObereGrenze
Debug.Print i, strFirmen(i)
Next i
End Sub
Wenn Sie ein Array in einer Funktion zusammenstellen und dieses als Rückgabewert verwenden möchten, muss das Array als Variant-Variable statt etwa
als String-Variable deklariert werden. Die Funktion,
welche das Variant-Array zusammenstellt, sieht so
aus:
Public Function FirmenEinlesen() As Variant
Dim db As DAO.Database
Dim rst As DAO.Recordset
Dim varFirmen() As Variant
Set db = CurrentDb
Set rst = db.OpenRecordset("SELECT Firma " _
& "FROM tblKunden", dbOpenDynaset)
Do While Not rst.EOF
ReDim Preserve varFirmen(rst.AbsolutePosition)
varFirmen(rst.AbsolutePosition) = rst!Firma
rst.MoveNext
Loop
FirmenEinlesen = varFirmen
End Function
Die folgende Prozedur ruft die Funktion FirmenEinlesen auf und schreibt den Rückgabewert in die Variable varFirmen. Die letzte Anweisung gibt testweise
ein Element des Arrays aus:
Die Prozedur deklariert zunächst zwei Variablen namens intUntereGrenze und intObereGrenze, welche
die Indexgrenzen aufnehmen. Diese werden mit den
durch die Funktionen LBound und UBound ermittelten Werten gefüllt. Anschließend durchläuft eine
For...Next-Schleife alle Werte von der untersten bis
zur obersten Indexgrenze.
Innerhalb der Schleife gibt eine Debug.Print-Anweisung den Wert der Laufvariablen sowie den Wert des
Arrays für diesen Indexwert aus.
Es gibt auch noch eine Kurzfassung, welche die Indexgrenzen direkt in der For...Next-Schleife angibt:
1/2013
Public Sub DynamischesArrayDurchlaufen_Kurz()
Dim i As Integer
Call ArrayAusRecordset
For i = LBound(strFirmen()) To _
UBound(strFirmen())
Debug.Print i, strFirmen(i)
Next i
End Sub
Seite 5
Public Sub ArrayAlsParameter()
Dim varFirmen() As Variant
varFirmen = FirmenEinlesen
Debug.Print varFirmen(90)
End Sub
Wenn Sie – entweder in der aufrufenden Prozedur
oder in der Funktion – das Array als String deklarieren, erzeugt dies einen Laufzeitfehler.
Zweidimensionale Arrays
Arrays können auch mehr als eine Dimension umfassen, zum Beispiel zwei. Ein zweidimensionales Array
deklarieren Sie ähnlich wie ein eindimensionales Ar-
www.access-basics.de
Access [basics]
VBA-Grundlagen
Programmieren mit Arrays
ray – mit dem Unterschied, dass Sie statt einer nun
zwei Elemente mit Indexbegrenzungen in Klammern
hinter dem Namen der Variablen eingeben.
In den folgenden Beispielen kümmern wir uns zunächst um statische Arrays und wollen Monate und
deren Abkürzungen im Array speichern. Die erste
Dimension benötigt also zwölf Elemente, die zweite
derer zwei. Bei einer nullbasierten Notation sieht
dies so aus:
Dim strMonate(11, 1) As String
Auch hier können Sie Bereiche angeben, was in diesem Beispiel zumindest für die erste Dimension
sinnvoll ist:
Dim strMonate(1 To 12, 0 To 1)
Die Indexgrenzen ermitteln Sie für die verschiedenen
Dimensionen, indem Sie als zweiten Parameter die
Nummer der Dimension angeben – hier interessanterweise 1-basiert.
Die folgende Anweisung gibt die Ober- und Untergrenze für die erste und zweite Dimension des oben
deklarierten Arrays aus, hier also 0, 11, 0, 1:
Debug.Print LBound(strMonate, 1), UBound(strMonate, 1),
LBound(strMonate, 2), UBound(strMonate, 2)
Mehrdimensionales Array füllen
Um ein solches Array zu füllen, weisen Sie der Variablen wieder den entsprechenden Wert zu. Diesmal
geben Sie allerdings nicht nur einen, sondern zwei
Indexwerte an. Dazu stellen wir uns die erste Dimension als Zeilen und die zweite als Spalten vor.
Die ersten Daten für den Monatsnamen und die Abkürzung weisen Sie dann wie folgt zu:
Public Function MonateEinlesen() As Variant
Dim strMonate(1 To 12, 0 To 1) As Variant
strMonate(1, 0) = "Januar"
strMonate(1, 1) = "Jan"
strMonate(2, 0) = "Februar"
strMonate(2, 1) = "Feb"
...
strMonate(12, 0) = "Dezember"
strMonate(12, 1) = "Dez"
MonateEinlesen = strMonate
End Function
1/2013
Mehrdimensionales Array lesen
Wenn Sie alle Elemente des obigen Arrays lesen
möchten, brauchen Sie zwei ineinander verschachtelte For...Next-Schleifen:
Public Sub MonateAusgeben()
Dim strMonate() As Variant
Dim i As Integer
Dim j As Integer
strMonate() = MonateEinlesen
For i = 1 To 12
For j = 0 To 1
Debug.Print strMonate(i, j),
Next j
Debug.Print
Next i
End Sub
Die äußere Schleife durchläuft jede Zeile des Arrays,
die innere jeweils die Spalten einer jeden Zeile. Die
innerhalb der inneren Schleife Debug.Print-Anweisung gibt also erst den kompletten Monatsnamen,
dann die Abkürzung aus – jeweils durch einen Tabulatorschritt voneinander getrennt. Damit die Prozedur nicht alle Elemente in einer Zeile ausgibt, folgt
nach jeder Zeile ein einfaches Debug.Print ohne Inhalt.
Maximale Anzahl der Dimensionen
Ein Array kann maximal 64 Dimensionen aufnehmen.
In der Regel verwendet man jedoch maximal drei Dimensionen, was praktisch einem Würfel entspricht.
Array leeren
Es gibt zwei Varianten, ein Array zu leeren: entweder Sie deklarieren es neu (und definieren auch die
Dimensionen erneut) oder Sie verwenden die EraseFunktion mit dem Namen des Arrays als Parameter
– zum Beispiel so:
Erase strFirmen()
Der Unterschied zeigt sich bei dynamisch gefüllten
Arrays: Wenn Sie ein dynamisches Array (also eines
ohne feste Angabe der Elementanzahl) neu deklarieren, werden nicht nur die Elemente geleert, sondern
auch alle Elemente gelöscht. Die Erase-Anweisung
hingegeben leert nur die Elementinhalte.
Seite 6
www.access-basics.de