über Arrays unter VBA wurde schon vieles geschrieben und gesagt. Dennoch gibt es praktische Hinweise, die man gelegentlich benötigt, nicht so einfach im Internet. Also werfen wir in diesem Beitrag einen genaueren Blick auf die Möglichkeiten, die Arrays bieten, und liefern einige neue Funktionen rund um die Felder zum temporären Speichern und Bereitstellen von Daten. Unter anderem sehen wir uns an, wie Sie die Inhalte von Arrays zu Debugging-Zwecken im Direktbereich oder in einer Excel-Tabelle ausgeben oder wie Sie prüfen, ob ein Array leer ist.
Array aus Datensatz füllen
Manchmal möchten Sie vielleicht die Daten eines oder mehrerer Felder eines Datensatzes in einem Array speichern, um diese Daten performant bereitzustellen.
Wenn Sie nur ein Feld eintragen möchten, reicht ein eindimensionales Array aus. Dieses füllen Sie mithilfe der Funktion RecordsetInArrayEindimensional aus Listing 1. Die Funktion erwartet das einzulesende Recordset mit dem Parameter rst.
Public Function RecordsetInArrayEindimensional(rst As DAO.Recordset) As Variant() Dim arr() As Variant Do While Not rst.EOF ReDim Preserve arr(rst.AbsolutePosition) arr(rst.AbsolutePosition) = rst.Fields(0).Value rst.MoveNext Loop RecordsetInArrayEindimensional = arr End Function
Listing 1: Einlesen eines Feldes eines Recordsets in ein eindimensionales Array
Es deklariert ein Array namens arr mit dem Datentyp Variant. In einer Do While-Schleife durchläuft die Funktion alle Datensätze des Recordsets und führt dabei zwei Anweisungen aus:
- das ReDimensionieren des Arrays entsprechend der aktuellen Position des Datensatzzeigers (AbsolutePosition) sowie
- das Zuweisen des Werts des ersten Felds des aktuellen Datensatzes an die entsprechende Position des Arrays.
Danach übergibt die Funktion das Array als Funktionswert an die aufrufende Routine. Diese sieht beispielsweise wie in Listing 2 aus. Die Routine füllt das Recordset-Objekt mit den Datensätzen der Tabelle tblArtikel, wobei nur das Feld Artikelname berücksichtigt wird.
Public Sub Test_RecordsetInArray() Dim db As DAO.Database Dim rst As DAO.Recordset Dim i As Integer Dim arr() As Variant Set db = CurrentDb Set rst = db.OpenRecordset("SELECT Artikelname FROM tblArtikel", dbOpenDynaset) arr = RecordsetInArrayEindimensional(rst) For i = LBound(arr) To UBound(arr) Debug.Print arr(i) Next i End Sub
Listing 2: Test zum Einlesen eines eindimensionalen Arrays aus einem Recordset
Dann ruft sie die Funktion Recordset-In-ArrayEindimensional auf und übergibt das Recordset als Parameter.
Um zu prüfen, ob alle Werte wie gewünscht in das Array eingelesen wurden, durchläuft die Routine anschließend in einer For…Next-Schleife alle Elemente des Arrays und gibt den jeweils aktuellen Wert im Direktbereich des VBA-Editors aus.
ReDim vorziehen
Wenn schon vorher klar ist, wie viele Elemente im Array landen sollen, können Sie dieses natürlich auch bereits vor dem Eintritt in die Do While-Schleife redimensionieren.
Der Versuch, dies direkt beim Deklarieren des Arrays zu erledigen, schlägt allerdings fehl – mit der Fehlermeldung Konstanter Ausdruck erforderlich, wie in Bild 1 zu erkennen.
Bild 1: Automatische Ergänzung eines Feldnamens
Also deklarieren wir das Array in einer neuen Version der Funktion (s. Listing 3) zunächst ohne Angabe der Anzahl der enthaltenen Elemente und ändern diese dann später mit der ReDim-Anweisung. Dieser übergeben wir den Ausdruck rst.RecordCount. Damit diese Eigenschaft des Recordsets immer den richtigen Wert liefert, verschieben wir den Datensatzzeiger zuvor mit MoveLast einmal an das Ende der Datensatzgruppe und mit MoveFirst wieder an die erste Position.
Public Function RecordsetInArrayEindimensional(rst As DAO.Recordset) As Variant() Dim arr() As Variant rst.MoveLast rst.MoveFirst ReDim arr(rst.RecordCount - 1) Do While Not rst.EOF arr(rst.AbsolutePosition) = rst.Fields(0).Value rst.MoveNext Loop RecordsetInArrayEindimensional = arr End Function
Listing 3: Einlesen eines eindimensionalen Arrays mit vorheriger Dimensionierung
Im Gegensatz zum ReDimensionieren innerhalb der Do While-Schleife entfällt hier natürlich das Preserve-Schlüsselwort der ReDim-Anweisung, denn das Array ist ja ohnehin noch leer.
Gleichzeitig beheben wir noch einen kleinen Fehler bei der Dimensionierung: Dort haben wir nämlich zunächst den Wert von rst.RecordCount als Obergrenze angegeben. Die Untergrenze ist jedoch, wenn nicht anders angegeben, 0. Das heißt, dass wir das Array um ein Element zu groß dimensionieren würden. Also ändern wir den Ausdruck für die obere Grenze in rst.RecordCount – 1.
Mehrdimensionale Arrays mit Daten füllen
Wenn Sie nun mehrere Felder in das Array einlesen möchten, müssen Sie dieses entsprechend anders dimensionieren – nämlich als zweidimensionales Array. Dazu deklarieren wir in der Funktion RecordsetInArrayMehrdimensional zunächst wieder das Array arr (s. Listing 4).
Public Function RecordsetInArrayMehrdimensional(rst As DAO.Recordset) As Variant() Dim arr() As Variant Dim fld As DAO.Field Dim i As Integer rst.MoveLast rst.MoveFirst ReDim arr(rst.RecordCount - 1, rst.Fields.Count - 1) Do While Not rst.EOF For Each fld In rst.Fields arr(rst.AbsolutePosition, i) = fld.Value i = i + 1 Next fld rst.MoveNext Loop RecordsetInArrayMehrdimensional = arr End Function
Listing 4: Einlesen eines mehrdimensionalen Arrays mit vorheriger Dimensionierung
Anschließend redimensioniert die Funktion es, und zwar mit der Anzahl der Datensätze in der ersten Dimension und mit der Anzahl der Felder in der zweiten. Danach durchläuft die Funktion in einer äußeren Do While-Schleife die Datensätze des Recordsets und in einer inneren Schleife die Felder – und zwar über die Fields-Auflistung des Recordsets. Innerhalb der Schleife füllt die Funktion das Array. Da fld keinen Index mitliefert, müssen wir immer noch umständlich eine Zählervariable namens i mitlaufen lassen, um die Position für die zweite Dimension anzugeben. Dies können wir auch noch eleganter erledigen, indem wir die innere Schleife als For…Next– statt als For…Each-Schleife implementieren:
Do While Not rst.EOF For i = 0 To rst.Fields.Count - 1 arr(rst.AbsolutePosition, i) _ = rst.Fields(i) Next i rst.MoveNext Loop
Die Funktion zum Testen dieser Funktion finden Sie in Listing 5. Die Prozedur erstellt diesmal ein Recordset mit den beiden Feldern ArtikelID und Artikelname der Tabelle tblArtikel und übergibt dieses als Parameter an die Funktion RecordsetInArrayMehrdimensional.
Public Sub Test_RecordsetInArrayMehrdimensional() Dim db As DAO.Database Dim rst As DAO.Recordset Dim i As Integer Dim j As Integer Dim arr() As Variant Set db = CurrentDb Set rst = db.OpenRecordset("SELECT ArtikelID, Artikelname FROM tblArtikel", _ dbOpenDynaset) arr = RecordsetInArrayMehrdimensional(rst) For i = LBound(arr, 1) To UBound(arr, 1) For j = LBound(arr, 2) To UBound(arr, 2) Debug.Print arr(i, j); Next j Debug.Print Next i End Sub
Listing 5: Testprozedur zum Einlesen mehrdimensionaler Arrays
Das Ergebnis durchläuft die Prozedur dann in zwei verschachtelten For…Next-Schleifen. Bei der Ermittlung der Arraygrenzen der beiden Dimensionen verwenden wir natürlich eine andere Form der LBound– und UBound-Funktionen. Die untere Grenze der ersten Dimension ermitteln wir beispielsweise mit LBound(arr, 1), die der zweiten Dimension mit LBound(arr, 2).
Innerhalb der inneren Schleife sollen die beiden Feldwerte des aktuellen Datensatzes ausgegeben werden. Damit diese in einer Zeile erscheinen, versehen wir die Debug.Print-Anweisungen noch mit einem folgenden Semikolon (;). Dies verhindert den Sprung in die folgende Zeile.
Allerdings würden so alle Elemente hintereinander in eine einzige Zeile geschrieben werden. Also müssen wir für jeden Datensatz, der ja in der äußeren Schleife durchlaufen wird, noch eine Debug.Print-Anweisung ohne Parameter einfügen.
Funktion zur Ausgabe von Arrays
Damit haben wir nun zwei Funktionen geschaffen, mit denen wir Arrays etwa auf Basis eines Recordsets füllen können. Allein die Ausgabe zur Prüfung des Arrays mit dem Durchlaufen der Elemente der einzelnen Dimensionen steckt noch in den beiden Testprozeduren.
Dabei wäre doch gerade die Ausgabe des Inhalts eines Arrays bei Entwickeln hilfreich, denn damit könnte man auf die Schnelle herausfinden, ob das Array auch die erwarteten Daten enthält. Also programmieren wir uns eine kleine Funktion, welche ein Array als Parameter erwartet und dieses im Direktbereich von Access ausgibt.
Die Prozedur heißt OutputVariant und ist in Listing 6 zu finden. Die Prozedur deklariert zwei Variablen namens i und j zum Durchlaufen der verschiedenen Dimensionen des Arrays sowie vier Variablen namens int1Min, int1Max, int2Min und int2Max, um die Unter- und Obergrenzen der Dimensionen des Arrays aufzunehmen.
Public Sub OutputVariant(varArray() As Variant) Dim i As Integer Dim j As Integer Dim int1Min As Integer Dim int1Max As Integer Dim int2Min As Integer Dim int2Max As Integer On Error Resume Next int1Min = LBound(varArray, 1) int1Max = UBound(varArray, 1) int2Min = LBound(varArray, 2) int2Max = UBound(varArray, 2) On Error GoTo 0 If int2Max = 0 Then For i = int1Min To int1Max Debug.Print varArray(i); Next i Debug.Print Else For i = int1Min To int1Max For j = int2Min To int2Max If varArray(i, j) = "" Then Debug.Print " x "; Else Debug.Print varArray(i, j); End If Next j Debug.Print Next i End If End Sub
Listing 6: Prozedur zur Ausgabe von Arrays
Diese Variablen füllt die Prozedur in den folgenden vier Zeilen, für die durch die Anweisung On Error Resume Next die Fehlerbehandlung deaktiviert wurde. Dies hat den Hintergrund, dass die Anzahl der Dimensionen ja auch eins sein kann. In diesem Fall führt der Zugriff etwa auf LBound(varArray, 2) zum Fehler Index außerhalb des gültigen Bereichs.
In diesem Fall behält die Variable int2Max den Wert 0, was im nächsten Schritt geprüft wird. Dann liegt offensichtlich ein eindimensionales Array vor, das innerhalb einer einfachen For…Next-Schleife über die Werte von int1Min bis int1Max ausgegeben werden kann.
Anderenfalls liegt wohl ein mehrdimensionales Array vor, für dessen Ausgabe zwei For…Next-Schleifen mit den Laufvariablen i und j durchlaufen werden.
Um leere Inhalte besser erkennen zu können, sollen diese durch ein x ersetzt werden. Dies können Sie gegebenenfalls ändern, wenn Sie ein anderes oder gar kein Zeichen wünschen. Auch hier wenden wir wieder den Trick mit dem Semikolon (;) hinter der Debug.Print-Anweisung an, um die Elemente der zweiten Dimension in einer Zeile auszugeben.
Diese Routine hat allerdings eine Einschränkung, die mit dem Datentyp des Arrays zusammenhängt. Wenn Sie es einmal mit der folgenden Prozedur testen, die ein Array mit dem Datentyp Integer füllt und dieses dann der Routine OutputVariant übergibt, erhalten wir den Fehler aus Bild 2:
Bild 2: Fehler bei falschen Array-Datentyp
Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...
den kompletten Artikel im PDF-Format mit Beispieldatenbank
diesen und alle anderen Artikel mit dem Jahresabo