Lektionen/Datenzugriff

DAO-Recordsets: Daten lesen & schreiben

Fortgeschritten12 Min. Lesezeit

Formulare und Abfragen bringen dich weit — aber sobald du Datensätze im Code durchlaufen, prüfen oder massenhaft ändern willst, brauchst du DAO-Recordsets. DAO (Data Access Objects) ist die native Zugriffsschicht von Access und arbeitet direkt und schnell mit deinen Tabellen. Diese Lektion zeigt dir das Lesen, das Schreiben und die typischen Stolperfallen.

CurrentDb: dein Einstiegspunkt

CurrentDb liefert dir ein Database-Objekt für die aktuell geöffnete Datenbank. Darüber öffnest du mit OpenRecordset einen Datensatzstrom — aus einer Tabelle, einer gespeicherten Abfrage oder direkt aus einem SQL-String:

Dim db As DAO.Database
Dim rs As DAO.Recordset

Set db = CurrentDb
Set rs = db.OpenRecordset("SELECT * FROM Kunden WHERE Ort = 'Berlin'")

Deklariere immer DAO.Recordset (nicht nur Recordset). Access kennt oft auch die ADO-Bibliothek, und beide haben ein Recordset-Objekt. Die explizite Angabe verhindert Verwechslungen.

Die drei Recordset-Typen

OpenRecordset nimmt als zweiten Parameter einen Typ. Die drei wichtigsten:

TypKonstanteWofür
TabledbOpenTableganze lokale Tabelle, erlaubt Seek (sehr schnell)
DynasetdbOpenDynasetaktualisierbare Ergebnismenge, auch über Joins/Abfragen
SnapshotdbOpenSnapshotschreibgeschützte Kopie, ideal nur zum Lesen
' Nur lesen? Snapshot ist am sparsamsten:
Set rs = db.OpenRecordset("Kunden", dbOpenSnapshot)

' Bearbeiten und Abfragen/Joins? Dynaset:
Set rs = db.OpenRecordset("SELECT * FROM Kunden", dbOpenDynaset)

Durch die Datensätze laufen: EOF und MoveNext

Ein frisch geöffnetes Recordset steht auf dem ersten Datensatz. Du läufst mit MoveNext durch und prüfst EOF (End Of File) als Abbruch:

Public Sub KundenAusgeben()
    Dim db As DAO.Database
    Dim rs As DAO.Recordset

    Set db = CurrentDb
    Set rs = db.OpenRecordset("SELECT * FROM Kunden", dbOpenSnapshot)

    Do Until rs.EOF
        Debug.Print rs!KundenID, rs!Nachname
        rs.MoveNext            ' unverzichtbar – sonst Endlosschleife!
    Loop

    rs.Close
    Set rs = Nothing
    Set db = Nothing
End Sub

Zwei Klassiker: Vergisst du MoveNext, läuft die Schleife endlos. Und wenn das Recordset leer sein könnte, prüfe vorher If Not (rs.BOF And rs.EOF) Then … — bei null Treffern sind BOF und EOF beide True.

Felder lesen: rs!Feld und rs.Fields()

Auf ein Feld des aktuellen Datensatzes greifst du auf zwei Arten zu. Die Ausrufezeichen-Schreibweise ist kurz, Fields() erlaubt dynamische Feldnamen:

Debug.Print rs!Nachname                 ' Bang-Notation, kurz
Debug.Print rs.Fields("Nachname")       ' explizit
Debug.Print rs.Fields("Nachname").Value ' .Value ist die Standardeigenschaft

Dim feldname As String
feldname = "Ort"
Debug.Print rs.Fields(feldname)         ' Feldname aus Variable

Enthält ein Feld Null, kracht es bei direkter Textverkettung. Fange das mit Nz ab, das Null in einen Ersatzwert wandelt:

Debug.Print Nz(rs!Telefon, "(keine Nummer)")

Neuen Datensatz anlegen: AddNew und Update

Zum Einfügen rufst du AddNew auf, füllst die Felder und schließt mit Update ab. Erst Update schreibt den Datensatz wirklich in die Tabelle:

Public Sub KundeAnlegen()
    Dim db As DAO.Database
    Dim rs As DAO.Recordset

    Set db = CurrentDb
    Set rs = db.OpenRecordset("Kunden", dbOpenDynaset)

    rs.AddNew
    rs!Vorname = "Anna"
    rs!Nachname = "Berger"
    rs!Ort = "Hamburg"
    rs.Update                  ' ohne Update ist alles verworfen!

    rs.Close
    Set rs = Nothing
    Set db = Nothing
End Sub

Bestehenden Datensatz ändern: Edit und Update

Um den aktuellen Datensatz zu ändern, schaltest du ihn mit Edit in den Bearbeitungsmodus, setzt Felder und bestätigst wieder mit Update:

Do Until rs.EOF
    If rs!Ort = "Berln" Then          ' Tippfehler korrigieren
        rs.Edit
        rs!Ort = "Berlin"
        rs.Update
    End If
    rs.MoveNext
Loop

Vergisst du Edit vor der Zuweisung, meldet Access „Update oder CancelUpdate ohne AddNew oder Edit". Und ohne abschließendes Update (oder mit rs.CancelUpdate) gehen deine Änderungen verloren.

Datensatz löschen: Delete

Delete entfernt den aktuellen Datensatz sofort — ohne Update. Danach steht der Cursor auf keinem gültigen Datensatz, deshalb rufst du direkt danach MoveNext auf:

Do Until rs.EOF
    If rs!Inaktiv = True Then
        rs.Delete
    End If
    rs.MoveNext                ' funktioniert auch nach Delete
Loop

Gezielt suchen: FindFirst und Seek

Statt alles zu durchlaufen, springst du direkt zum Treffer. FindFirst arbeitet auf Dynasets/Snapshots und nimmt eine Kriterien-Zeichenkette (wie eine WHERE-Bedingung ohne das Wort WHERE). Ob etwas gefunden wurde, verrät NoMatch:

rs.FindFirst "Nachname = 'Berger'"
If Not rs.NoMatch Then
    Debug.Print "Gefunden: "; rs!KundenID
Else
    Debug.Print "Kein Treffer."
End If

Bei Text-Kriterien mit variablem Wert setzt du Anführungszeichen sauber, etwa mit Chr(34) oder doppelten Hochkommata:

Dim name As String
name = "O'Brien"
rs.FindFirst "Nachname = " & Chr(34) & name & Chr(34)

Seek ist deutlich schneller, funktioniert aber nur auf dbOpenTable und braucht einen Index. Du setzt den Index und übergibst den Suchwert:

Set rs = db.OpenRecordset("Kunden", dbOpenTable)
rs.Index = "PrimaryKey"
rs.Seek "=", 42
If Not rs.NoMatch Then Debug.Print rs!Nachname

Die RecordCount-Falle

rs.RecordCount gibt nicht sofort die Gesamtzahl zurück. Direkt nach dem Öffnen kennt DAO nur die bereits „berührten" Datensätze — meist 1 (oder 0 bei leerem Ergebnis). Erst wenn du einmal ans Ende springst, stimmt der Wert:

Set rs = db.OpenRecordset("Kunden", dbOpenDynaset)

Debug.Print rs.RecordCount          ' oft nur 1 – irreführend!

If Not (rs.BOF And rs.EOF) Then
    rs.MoveLast                     ' zwingt DAO, alle Datensätze zu zählen
    Debug.Print rs.RecordCount      ' jetzt die echte Anzahl
    rs.MoveFirst                    ' zurück, falls du danach durchlaufen willst
End If

Für eine reine Zählung ist ein DCount oder SELECT COUNT(*) ohnehin oft schneller, als ein ganzes Recordset zu öffnen.

Sauber schließen — auch im Fehlerfall

Recordset und Database gehören nach Gebrauch geschlossen und freigegeben. Close gibt die Sperren frei, Set … = Nothing löst die Objektreferenz:

Public Sub SauberArbeiten()
    Dim db As DAO.Database
    Dim rs As DAO.Recordset

    On Error GoTo Aufraeumen

    Set db = CurrentDb
    Set rs = db.OpenRecordset("Kunden", dbOpenSnapshot)

    Do Until rs.EOF
        ' … Verarbeitung …
        rs.MoveNext
    Loop

Aufraeumen:
    ' Prüfen, ob rs überhaupt gesetzt wurde – sonst kracht Close
    If Not rs Is Nothing Then
        rs.Close
        Set rs = Nothing
    End If
    Set db = Nothing

    If Err.Number <> 0 Then MsgBox "Fehler: " & Err.Description
End Sub

Das If Not rs Is Nothing Then ist wichtig: Springt der Fehler auf, bevor OpenRecordset lief, ist rs noch Nothing, und ein rs.Close würde einen zweiten Fehler auslösen. Set db = Nothing genügt für CurrentDb — ein Close auf die aktuelle Datenbank darfst du nicht aufrufen.

Zusammengefasst

  • CurrentDb.OpenRecordset(...) öffnet den Datensatzstrom; wähle dbOpenSnapshot zum Lesen, dbOpenDynaset zum Ändern, dbOpenTable für Seek.
  • Durchlaufen mit Do Until rs.EOF … rs.MoveNext … Loop; MoveNext nie vergessen, bei leerem Ergebnis BOF And EOF prüfen.
  • Ändern über AddNew/Edit + Update, löschen mit Delete (ohne Update); Feldzugriff via rs!Feld oder rs.Fields(...), Null mit Nz abfangen.
  • RecordCount stimmt erst nach MoveLast — vorher zeigt es oft nur 1.
  • Immer rs.Close + Set rs = Nothing, im Fehlerfall mit If Not rs Is Nothing absichern; CurrentDb selbst nicht schließen.
Nächste Lektion
SQL aus VBA ausführen