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