Lektionen/Datenzugriff

SQL aus VBA ausführen

Fortgeschritten11 Min. Lesezeit

Nicht jede Datenänderung braucht ein gebundenes Formular. Oft willst du aus VBA heraus einfach ein paar Datensätze anlegen, aktualisieren oder löschen — eine Aktionsabfrage. Access bietet dafür zwei Wege: DoCmd.RunSQL und CurrentDb.Execute. Sie sehen ähnlich aus, verhalten sich aber unterschiedlich. Dieser Beitrag zeigt, welchen du wann nimmst und wie du die SQL-Strings sauber baust.

DoCmd.RunSQL — der einfache Weg

DoCmd.RunSQL führt eine Aktionsabfrage aus. Es funktioniert nur mit INSERT, UPDATE, DELETE und SELECT ... INTO — keine SELECT-Abfragen, die Daten zurückgeben.

DoCmd.RunSQL "UPDATE Kunden SET Aktiv = False WHERE LetzterKauf < #01/01/2024#"

Der Haken: RunSQL zeigt vor der Ausführung eine Bestätigungsmeldung („Sie sind im Begriff, n Zeilen zu aktualisieren"). In einer Automatisierung stört das. Viele schalten das dann mit SetWarnings ab:

' Antipattern — nicht nachmachen:
DoCmd.SetWarnings False
DoCmd.RunSQL "DELETE FROM Protokoll WHERE Datum < #01/01/2024#"
DoCmd.SetWarnings True

Warum das gefährlich ist: Wenn zwischen den beiden Zeilen ein Fehler auftritt, bleibt SetWarnings auf False — und ab dann verschluckt Access alle Warnungen der Sitzung, auch wichtige. Der Anwender bekommt stumme Datenverluste. Nutze stattdessen Execute.

CurrentDb.Execute — der bessere Weg

Execute gehört zum DAO-Database-Objekt. Es zeigt keine Bestätigungs- meldungen und braucht deshalb kein SetWarnings. Wichtiger noch: mit der Option dbFailOnError löst es einen echten VBA-Laufzeitfehler aus, wenn etwas schiefgeht (z. B. Verstoß gegen die referentielle Integrität).

CurrentDb.Execute _
    "DELETE FROM Protokoll WHERE Datum < #01/01/2024#", _
    dbFailOnError

Ohne dbFailOnError scheitert eine fehlerhafte Abfrage stillschweigend — Access ändert einfach nichts und meldet nichts. Deshalb: dbFailOnError gehört immer dazu.

RunSQL vs. Execute im Vergleich

EigenschaftDoCmd.RunSQLCurrentDb.Execute
Bestätigungsmeldungja (nervt)nein
Braucht SetWarningsjanein
Fehler abfangbarschwerja, mit dbFailOnError
Betroffene Zeilen abfragbarneinja, RecordsAffected
Gehört zuMakro-/DoCmd-WeltDAO

Fazit: Für Aktionsabfragen aus VBA ist Execute fast immer die richtige Wahl.

RecordsAffected — wie viele Zeilen?

Nach einem Execute verrät dir das Database-Objekt, wie viele Datensätze betroffen waren. Dazu brauchst du eine Objektvariable, keinen Einzelaufruf von CurrentDb (jeder CurrentDb-Aufruf liefert eine neue Instanz):

Public Sub AltKundenDeaktivieren()
    Dim db As DAO.Database
    Dim sql As String

    Set db = CurrentDb
    sql = "UPDATE Kunden SET Aktiv = False " & _
          "WHERE LetzterKauf < #01/01/2024#"

    db.Execute sql, dbFailOnError
    Debug.Print db.RecordsAffected & " Kunde(n) deaktiviert"

    Set db = Nothing
End Sub

SQL-Strings sauber bauen

Sobald du Werte aus Variablen in den SQL-Text einsetzt, wird das Quoten wichtig. Access-SQL hat feste Regeln für die einzelnen Datentypen:

DatentypBegrenzerBeispiel
Zahlkeiner... WHERE Preis > 100
Texteinfache Anführungszeichen '... WHERE Name = 'Meier'
DatumRauten, US-Format #mm/dd/yyyy#... WHERE Datum = #12/31/2024#

Der häufigste Fehler bei Datumsangaben: Access-SQL erwartet immer das US-Format mm/dd/yyyy, egal welche Ländereinstellung dein Windows hat. Für ein Date-Feld aus VBA baust du das mit Format zuverlässig auf:

Public Sub BuchungAnlegen(ByVal kunde As String, _
                          ByVal betrag As Currency, _
                          ByVal wann As Date)
    Dim db As DAO.Database
    Dim sql As String

    Set db = CurrentDb
    sql = "INSERT INTO Buchungen (Kunde, Betrag, Datum) VALUES (" & _
          "'" & Replace(kunde, "'", "''") & "', " & _
          Replace(Str(betrag), ",", ".") & ", " & _
          "#" & Format(wann, "mm\/dd\/yyyy") & "#)"

    db.Execute sql, dbFailOnError
    Set db = Nothing
End Sub

Drei Details, die hier zählen:

  • Text: in ' einfassen. Enthält der Wert selbst ein ' (z. B. O'Brien), wird es durch Replace(kunde, "'", "''") verdoppelt — sonst bricht der String.
  • Zahl: in VBA nutzt Str bei deutscher Einstellung evtl. ein Komma; SQL will einen Punkt. Deshalb Replace(..., ",", ".").
  • Datum: mit Format(wann, "mm\/dd\/yyyy") erzwingst du das US-Format und die Schrägstriche, unabhängig von den Systemeinstellungen.

SQL-Injection: warum String-Basteln riskant ist

Das obige Replace für Anführungszeichen ist nicht bloß Kosmetik. Wenn du Benutzereingaben ungeprüft in SQL einsetzt, kann jemand über ein Textfeld eigene SQL-Befehle einschleusen — SQL-Injection. Ein Name wie x' OR '1'='1 würde ein WHERE aushebeln.

Merke: String-Verkettung ist fehleranfällig (Quoting, Datumsformate) und unsicher. Für alles, was Benutzereingaben verarbeitet, sind Parameterabfragen die bessere Lösung — sie kümmern sich um Quoting und Datentypen automatisch. Mehr dazu in der nächsten Lektion.

Zusammengefasst

  • DoCmd.RunSQL zeigt Bestätigungsmeldungen; CurrentDb.Execute nicht — nimm Execute.
  • Immer dbFailOnError mitgeben, sonst scheitern Abfragen stillschweigend.
  • Finger weg vom SetWarnings False-Antipattern: bleibt es hängen, verschluckt Access alle Warnungen.
  • db.RecordsAffected liefert nach Execute die Anzahl betroffener Zeilen — dafür CurrentDb einmal in eine Variable holen.
  • Werte richtig quoten: Text mit ' (und '' verdoppeln), Datum mit #mm/dd/yyyy#, Zahlen mit Punkt. Bei Benutzereingaben besser Parameterabfragen.
Nächste Lektion
Parameterabfragen mit QueryDef