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 nurRecordset). Access kennt oft auch die ADO-Bibliothek, und beide haben einRecordset-Objekt. Die explizite Angabe verhindert Verwechslungen.
Die drei Recordset-Typen
OpenRecordset nimmt als zweiten Parameter einen Typ. Die drei wichtigsten:
| Typ | Konstante | Wofür |
|---|---|---|
| Table | dbOpenTable | ganze lokale Tabelle, erlaubt Seek (sehr schnell) |
| Dynaset | dbOpenDynaset | aktualisierbare Ergebnismenge, auch über Joins/Abfragen |
| Snapshot | dbOpenSnapshot | schreibgeschü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 vorherIf Not (rs.BOF And rs.EOF) Then …— bei null Treffern sindBOFundEOFbeideTrue.
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
Editvor der Zuweisung, meldet Access „Update oder CancelUpdate ohne AddNew oder Edit". Und ohne abschließendesUpdate(oder mitrs.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 Thenist wichtig: Springt der Fehler auf, bevorOpenRecordsetlief, istrsnochNothing, und einrs.Closewürde einen zweiten Fehler auslösen.Set db = Nothinggenügt fürCurrentDb— einCloseauf die aktuelle Datenbank darfst du nicht aufrufen.
Zusammengefasst
CurrentDb.OpenRecordset(...)öffnet den Datensatzstrom; wähledbOpenSnapshotzum Lesen,dbOpenDynasetzum Ändern,dbOpenTablefürSeek.- Durchlaufen mit
Do Until rs.EOF … rs.MoveNext … Loop;MoveNextnie vergessen, bei leerem ErgebnisBOF And EOFprüfen. - Ändern über
AddNew/Edit+Update, löschen mitDelete(ohne Update); Feldzugriff viars!Feldoderrs.Fields(...),NullmitNzabfangen. RecordCountstimmt erst nachMoveLast— vorher zeigt es oft nur 1.- Immer
rs.Close+Set rs = Nothing, im Fehlerfall mitIf Not rs Is Nothingabsichern;CurrentDbselbst nicht schließen.