ü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