Warum noch ein Access-Buch?
Für wen ist das Buch?
Jetzt bestellen
+ direkter Download des eBooks!
Nur EUR 59,95!
Fehler gefunden?
Bitte melden!
Wünsche an das Buch?
Her damit!
Was denken die Leser über dieses Buch?
Lesen Sie aktuelle Rezensionen!
Kapitel des noch nicht veröffentlichten Buchs zum Downloaden, Probelesen und Kommentieren
Beispieldatenbanken
Zusätzliches Material

Das Buch im HTML-Format

Für unbestimmte Zeit bieten Addison-Wesley und André Minhorst den kompletten Inhalt des Buchs als Download an. Schauen Sie rein und informieren Sie sich über den Inhalt! Und wenn Ihnen das Buch nützlich erscheint und Sie glauben, dass Sie etwas gelernt haben oder durch das Gelesene sogar etwas Zeit und somit Geld bei Ihrer Arbeit einsparen konnten, können Sie sich ja beim Autor und beim Verlag revanchieren - beispielsweise durch den Kauf dieses Buchs.

Am schönsten wäre es natürlich, wenn Sie das Buch direkt hier bestellen - Sie erhalten das Buch dann direkt vom Verlag, und der Autor und Verlag haben dann noch mehr davon, als wenn Sie es anderswo kaufen.

Danke für Ihr Interesse!

14.5 VBA

14.5.1 Performance von VBA-Code optimieren

Das Kapitel 7, »VBA«, enthält unter anderem Informationen über das Schreiben optimalen VBA-Codes. Die dort dargestellten Möglichkeiten beziehen sich nicht primär auf das Erreichen einer besseren Performance, sondern einer besseren Struktur, Lesbarkeit und Wiederverwendbarkeit. Tipps für eine bessere Performance finden Sie in den folgenden Abschnitten.

Variablen und Datentypen

Was für die Festlegung der Datentypen von Tabellenfeldern gilt, trifft auch für die Deklaration von Variablen in VBA-Routinen zu. Sie sollten immer den kleinstmöglichen Datentyp wählen.

Dazu gehört, dass Sie überhaupt einen Datentyp festlegen, denn sonst verwendet Access automatisch den Datentyp Variant. Dieser kann beliebige Daten aufnehmen, belegt entsprechenden Speicherplatz und nötigt VBA intern, permanent zwischen verschiedenen Datentypen zu konvertieren. Um sicherzugehen, dass Sie jeder Variablen einen Datentyp zuweisen, schreiben Sie die folgende Zeile in den Kopf ihrer Module:

Option Explicit

Ist eine Variable nicht deklariert, kompiliert Access das Modul nicht (siehe Abbildung 14.9).

Abbildung 14.9: Kein Kompilieren ohne Variablendeklaration

Bei der Wahl des richtigen Datentyps für eine Variable hilft Tabelle 14.1 weiter - verwenden Sie einfach den Datentyp, mit dessen Wertebereich Sie vermutlich auskommen.

Dabei gibt es wiederum eine Ausnahme: Wenn Sie mit ganzzahligen Variablen rechnen möchten, verwenden Sie möglichst den Datentyp Long. Windows als 32bit-System kann mit diesem Zahlentyp schneller rechnen.

Early Binding statt Late Binding

Deklarieren Sie Objektvariablen direkt mit dem konkreten Objekttyp und nicht mit dem Datentyp Object. Dadurch kann Access Early Binding verwenden - die Bibliothek mit den benötigten Objekten, Methoden und Eigenschaften ist Access bekannt und ermöglicht so einen schnelleren Zugriff auf die enthaltenen Objekte. Dazu müssen Sie zuvor einen Verweis auf die entsprechende Objektbibliothek erstellen (siehe Abbildung 14.10).

Die folgende Routine zeigt ein Beispiel für Early Binding:

Public Function EarlyBinding()
    Dim cbr As CommandBar
    For Each cbr In Application.CommandBars
        Debug.Print cbr.Name

    Next cbr
    Set cbr = Nothing
End Function

Listing 14.5: Beispiel für das Early Binding von Objekten ...

Wenn Sie die Routine ohne Early Binding realisieren wollten, würde das wie im folgenden Listing aussehen:

Public Function LateBinding()
    Dim cbr As Object
    For Each cbr In Application.CommandBars
        Debug.Print cbr.Name
    Next cbr
    Set cbr = Nothing
End Function

Listing 14.6: ... und die gleiche Funktionalität mit Late Binding

y Binding liefert einen weiteren Vorteil: Da die Objektbibliothek und die enthaltenen Elemente bekannt sind, können Sie bei der Eingabe IntelliSense nutzen.

Das ist nicht nur für die Auswahl der Methoden und Eigenschaften interessant, sondern gerade für die Verwendung von Parametern. Sie brauchen dort nicht die nackten Zahlenwerte zu verwenden, sondern können auf die entsprechenden Bezeichnungen zurückgreifen.

Objektvariablen verwenden, wenn ein Objekt mehr als einmal referenziert wird

In vielen Fällen ist es gute Gewohnheit, ein Objekt zu deklarieren und zu instanzieren, bevor Sie auf dessen Elemente zugreifen. Das beste Beispiel dafür ist sicher die Verwendung von db und rst für Database- und Recordset-Objekte:

Public Sub ObjekteMehrfachNutzen()
    Dim db As DAO.Database
    Dim rst As DAO.Database
    Set db = CurrentDb
    Set rst = db.OpenRecordset("tblKontakte", dbOpenDynaset)
    Debug.Print rst!Vorname
    Debug.Print rst!Nachname
    rst.Close
    Set rst = Nothing
    Set db = Nothing
End Sub

Listing 14.7: Verwendung einer Objektvariablen

Abbildung 14.10: Ein Verweis auf die passende Objektbibliothek ist Voraussetzung für die Verwendung von Early Binding

Wann immer Sie mehr als einmal auf ein Objekt zugreifen, sollten Sie es mit einer geeigneten Objektvariablen referenzieren. Wenn Sie beispielsweise mehrere Steuerelemente eines Formulars auslesen, legen Sie einfach eine Referenz auf das Formular an.

Die erste Variante einer Routine zum Auslesen der Textfelder eines Formulars stellt für jeden einzelnen Zugriff die Verbindung zum Formular her:

Public Function FormularAuslesenOhneReferenz()
    Dim strVorname As String
    Dim strNachname As String
    strVorname = Forms!frmKontakte!Vorname
    strNachname = Forms!frmKontakte!Nachname
End Function

Listing 14.8: Direkter Zugriff auf die Eigenschaften eines Formulars

Die zweite Variante erfordert zwar ein wenig mehr Code, ist jedoch schneller:

Public Function FormularAuslesenMitReferenz()
    Dim frm As Form
    Dim strVorname As String
    Dim strNachname As String
    Set frm = Forms!frmKontakte
    strVorname = frm!Vorname
    strNachname = frm!Nachname
    Set frm = Nothing
End Function

Listing 14.9: Zugriff auf Steuerelemente eines Formulars via Objektvariable

Me statt Verweis auf Forms- oder Reports-Auflistung

Verwenden Sie den Ausdruck Me statt der Variante über die Formular- oder Berichts-Auflistung. Die folgende Routine ist die langsamere Möglichkeit:

Public Function FormularbezugMitFormsFormularname()
    Dim strVorname As String
    strVorname = Forms!frmKontakte!Vorname
End Function

Listing 14.10: Die langsame Variante für den Zugriff auf objektinterne Elemente ...

Die nächste Variante verwendet das Schlüsselwort Me und ist um rund 40% schneller:

Public Function FormularbezugMitMe()
    Dim strVorname As String
    strVorname = Me!Vorname
End Function

Listing 14.11: ... und die schnellere Variante

Variablen verwenden, wenn konstante Werte mehr als einmal ermittelt werden

Wenn Sie mehrmals im Code die gleiche Funktion wie etwa eine Domänen-Funktion (Dlookup, Dmax, Dcount) verwenden, deren Ergebnis vermutlich in der Zwischenzeit nicht verändert wird, speichern Sie das Ergebnis in einer Variablen.

Variablen auf jeden Fall verwenden, wenn der Inhalt Start- oder End-Wert einer Schleife markiert

Obiger Tipp ist in einem Fall auch dann anzuwenden, wenn der Inhalt der Variablen scheinbar nur einmal verwendet wird - und zwar innerhalb einer Schleife. Anderenfalls wird der Wert bei jedem Schleifendurchlauf neu berechnet. Das folgende Beispiel sieht sehr harmlos aus, aber der Inhalt des Feldes txtAnzahl wird mit jedem Durchlauf neu ausgelesen:

For i = 1 to Me!txtAnzahl
...

Verwenden Sie also stattdessen folgende Codezeilen, um den benötigten Wert zuvor in einer Variablen zu speichern:

Dim lngAnzahl As Long
lngAnzahl = Me!txtAnzahl
For i = 1 to lngAnzahl
...

Zeichenketten-Funktionen sparsam verwenden

Zeichenketten-Funktionen sind sehr aufwändig. Versuchen Sie, diese möglichst sparsam einzusetzen.

String-Variante von Zeichenketten-Funktionen verwenden

Es gibt jede Zeichenketten-Funktion in zwei Ausführungen: mit und ohne angehängtes Dollar-Zeichen ($). Der Unterschied der Funktionen liegt im Datentyp des Rückgabewertes: Die Version der Textfunktionen mit dem Dollar-Zeichen liefert einen String zurück, die ohne Dollar-Zeichen einen Wert vom Datentyp Variant.

Da man das Ergebnis von Zeichenketten-Funktionen meistens in einer String-Variablen speichert, ist bei der Verwendung der Variant-Version der Zeichenketten-Funktion noch eine zusätzliche interne Umwandlung des Datentyps erforderlich.

Zeichenketten vergleichen mit StrComp

Das gängige Mittel zum Vergleichen zweier Zeichenketten ist die Verwendung des Gleichheitszeichens als Operator. VBA bietet eine dazu besser geeignete Funktion namens StrComp. Die Funktion erwartet die zu vergleichenden Zeichenketten und gegebenenfalls eine Einstellung für die Vergleichsmethode.

Leere Zeichenkette testen über die Länge der Zeichenkette

Ob eine Zeichenkette leer ist, prüfen Sie üblicherweise durch einen Vergleich mit der Zeichenkette "". Das ist nicht der schnellste Weg. Performanter ist es, die Länge der Zeichenkette zu ermitteln und diesen Wert mit 0 zu vergleichen.

Leere Zeichenkette per vbNullString zuweisen

Eine Zeichenkette leeren Sie normalerweise durch Zuweisen einer leeren Zeichenkette:

str = ""

Etwas schneller geht es mit der Konstanten vbNullString:

str = vbNullString

Zeichenverkettung mit dem kaufmännischen Und vermeiden

Wenn zwei Zeichenketten miteinander verknüpft werden müssen, dann ist das &-Zeichen unvermeidlich.

Es ist aber sehr beliebt, längere Zeichenketten der Übersichtlichkeit halber auf mehrere Codezeilen zu verteilen - vor allem bei dynamisch zusammengesetzten SQL-Ausdrücken:

Public Function UndOperator1()
    Dim strSQL As String
    strSQL = "SELECT tblKontakte.Vorname, tblKontakte.Nachname "
    strSQL = strSQL & "FROM tblKontakte "
    strSQL = strSQL & "WHERE tblKontakte.Vorname = 'André' "
    strSQL = strSQL & "AND tblKontakte.Nachname = 'Minhorst'"
End Function

Listing 14.12: Zusammengesetzter SQL-Ausdruck

Die folgende Variante ist schneller, weil die Zeichenkette nicht mehr verknüpft werden muss. Wenn Sie also alles aus Ihrer Datenbankanwendung herausholen möchten, wandeln Sie alle Ausdrücke, die ohne Not mit dem Kaufmanns-Und verbunden sind, wieder in einen einzigen Ausdruck um:

Public Function UndOperator2()
    Dim strSQL As String
    strSQL = "SELECT tblKontakte.Vorname,..." _
End Function

Listing 14.13: SQL-Ausdruck in einer Zeile

Statische oder dynamische Arrays?

Die Größe eines Arrays können Sie bereits bei der Deklaration festlegen (statisches Array) oder erst später (dynamisches Array). Bei der statischen Variante geben Sie die Anzahl der vorgesehenen Einträge bereits bei der Deklaration an:

Public Function ArrayStatisch()
    Dim i As Long
    Dim lng(10000) As Long
    For i = 1 To 10000
        lng(i) = i
    Next i
End Function

Listing 14.14: Verwendung eines statischen Arrays

Die dynamische Variante sieht etwas anders aus. Hier geben Sie bei der Deklaration noch keinen Wert an und redimensionieren die Größe des Arrays bei Bedarf.

Public Function ArrayDynamisch()
    Dim i As Long
    Dim lng() As Long
    For i = 1 To 10000
        ReDim Preserve lng(i)
        lng(i) = i
    Next i
End Function

Listing 14.15: Verwendung eines dynamischen Arrays

Die beiden Varianten haben Vor- und Nachteile. Die statische Variante reserviert von vornherein ein Array von einer Größe, die Sie vielleicht gar nicht benötigen.

Und falls doch, wird im Durchschnitt die Hälfte des reservierten Speicherplatzes nicht gebraucht.

Andererseits kostet die ständige Redimensionierung wesentlich mehr Zeit - hier ist also zwischen benötigtem Speicherplatz und Geschwindigkeit zu entscheiden.

Logische Ausdrücke vereinfachen

Logische Ausdrücke landen oft in den übersichtlichen If...Then-Konstrukten. Das sieht dann beispielsweise so aus:

Public Function IfThen1()
    Dim i As Integer
    Dim bol As Boolean
    If i = 1 Then
        bol = True
    Else
        bol = False
    End If
End Function

Listing 14.16: Ermitteln eines Boolean-Wertes per If...Then-Konstrukt ...

Etwas schneller geht es mit der folgenden Variante, die außerdem noch einige Zeilen Code einspart:

Public Function IfThen2()
    Dim i As Integer
    Dim bol As Boolean
    bol = (i = 1)
End Function

Listing 14.17: ... und über die direkte Auswertung des Ausdrucks

Sollte Ihnen die Übersicht bei der zweiten Variante etwas zu kurz kommen, können Sie allerdings auch auf den hier relativ geringen Geschwindigkeitsvorteil verzichten.

Boolean -Werte switchen

Wenn Sie einer Boolean-Variablen, die den Wert True enthält, den Wert False zuweisen möchten oder umgekehrt, verwenden Sie vermutlich die folgende Variante:

Public Function Invertieren1()
    Dim x As Boolean
    If x = True Then
        x = False
    Else
        x = True
    End If
End Function

Listing 14.18: Lange Variante der Invertierung einer Boolean-Variablen ...

Etwas schneller und Platz sparender ist die folgende Variante:

Public Function Invertieren2()
    Dim x As Boolean
    x = Not x
End Function

Listing 14.19: ... und hier die kürzere, etwas schnellere Lösung

Performance von Schleifen mit fester Durchlaufzahl

Wenn Sie die Anzahl Durchläufe einer Schleife kennen, ist eine For...Next-Schleife schneller als eine Do...While- oder Do...Loop-Schleife. Die beiden folgenden Funktionen durchlaufen eine Schleife jeweils tausend Mal. Die zweite Variante enthält bereits im nackten Zustand eine Anweisung mehr, die den Zähler erhöht. Dadurch ist sie wesentlich langsamer als die erste Variante. Der Unterschied wird allerdings immer kleiner, je mehr Anweisungen sich innerhalb der Schleifen befinden.

Public Function Schleife1()
    Dim i As Integer
    For i = 1 To 1000
        'Etwas tun...
    Next i
End Function

Listing 14.20: Diese For...Next-Schleife erledigt die gleiche Arbeit ...

Public Function Schleife2()
    Dim i As Integer
    Do While Not i = 1000
        'Etwas tun...
        i = i + 1
    Loop
End Function

Listing 14.21: ... wie diese Do...While-Schleife, nur schneller

If Then oder IIf?

Die IIf-Funktion wird gerne als einzeiliger Ersatz für einfache If...Then-Konstrukte verwendet. Sie wertet den im ersten Parameter angegebenen Ausdruck aus und gibt für das Ergebnis True den Wert des zweiten Parameters und für das Ergebnis False den dritten Parameter zurück.

Diese Funktion hat aber gegenüber If...Then entscheidende Nachteile: Die IIf-Funktion wertet immer beide möglichen Antworten aus. Dadurch wird erstens Zeit vergeudet und zweitens können nicht eingeplante Nebeneffekte auftreten.

Das folgende Beispiel veranschaulicht dies. Die IIf-Anweisung soll das Ergebnis der Division der Variablen a und b ausgeben, aber nur, wenn b nicht 0 ist. Ist das doch der Fall, würde die Division einen Division-durch-Null-Fehler erzeugen, was durch die IIf-Funktion abgefangen werden soll. Das funktioniert aber nicht: Obwohl die Bedingung erfüllt ist und eigentlich der Text Division durch 0 ausgegeben werden müsste, erscheint die Fehlermeldung, die eigentlich verhindert werden sollte.

Public Function Division()
    Dim a As Integer
    Dim b As Integer
    a = 1
    b = 0
    Debug.Print IIf(b = 0, "Division durch 0", a / b)
End Function

Listing 14.22: Unerwünschte Nebeneffekte mit der IIf-Funktion

Die gleiche Variante mit einem If...Then-Konstrukt sieht folgendermaßen aus. Performancetests ergaben keine wesentlichen Vorteile für eine der beiden Varianten, sodass Sie die If...Then-Variante wegen der besseren Kalkulierbarkeit einsetzen sollten.

Public Function Division2()
    Dim a As Integer
    Dim b As Integer
    a = 1
    b = 0
    If b = 0 Then
        Debug.Print "Division durch Null"
    Else
        Debug.Print a / b
    End If
End Function

Listing 14.23: Diese Variante bringt keine bösen Überraschungen

Nächster Abschnitt:

14.5.2 Punkt oder Ausrufezeichen

© 2006-2008 André Minhorst Alle Rechte vorbehalten.