{"id":55000471,"date":"2025-04-01T00:00:00","date_gmt":"2025-07-02T14:48:09","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=471"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"ADODB_Datenzugriff_mit_Recordsets","status":"publish","type":"post","link":"https:\/\/vbentwickler.de\/ADODB_Datenzugriff_mit_Recordsets\/","title":{"rendered":"ADODB: Datenzugriff mit Recordsets"},"content":{"rendered":"<p><b>Recordsets sind zentrale Bestandteile beim Zugriff auf Daten mit ADODB. Sie bieten umfangreiche Funktionen zur Navigation, Bearbeitung, Filterung und Analyse von Daten. In diesem Artikel zeigen wir eine vollst&auml;ndige &Uuml;bersicht aller Eigenschaften und Methoden des Recordset-Objekts und erl&auml;utern diese jeweils ausf&uuml;hrlich mit praktischen Beispielen. Besonderes Augenmerk legen wir auf die Ereignisse eines Recordsetes. Im Gegensatz zum DAO-Recordset bietet das ADODB-Recordset n&auml;mlich die M&ouml;glichkeit, auf verschiedene Ereignisse zu reagieren &#8211; beispielsweise auf &Auml;nderungen im Datensatz.<\/b><\/p>\n<h2>Beispieldatenbank<\/h2>\n<p>Die Beispiele dieses Artikels findest Du in der Beispieldatenbank <b>ADODB_Recordset_AlleEigenschaften.accdb<\/b>.<\/p>\n<h2>&Uuml;bersicht: Eigenschaften und Methoden<\/h2>\n<p>Im Folgenden geben wir eine Kurzbeschreibung aller verf&uuml;gbaren Eigenschaften und Methoden des <b>Recordset<\/b>-Objekts, bevor wir diese im Einzelnen detailliert erl&auml;utern:<\/p>\n<ul>\n<li><b>AbsolutePage<\/b>: Liefert oder setzt die aktuelle Seite bei Seitennavigation.<\/li>\n<li><b>AbsolutePosition<\/b>: Position des aktuellen Datensatzes im Recordset.<\/li>\n<li><b>ActiveCommand<\/b>: Liefert das zugeh&ouml;rige <b>Command<\/b>-Objekt.<\/li>\n<li><b>ActiveConnection<\/b>: Verbindung, mit der das Recordset verkn&uuml;pft ist.<\/li>\n<li><b>AddNew\/Update\/CancelUpdate<\/b>: Neue Datens&auml;tze hinzuf&uuml;gen und speichern.<\/li>\n<li><b>BOF\/EOF<\/b>: Gibt an, ob sich der Datensatzmarkierer vor dem ersten oder nach dem letzten Datensatz befindet.<\/li>\n<li><b>Bookmark\/CompareBookmarks<\/b>: Zum Speichern und Vergleichen von Positionen.<\/li>\n<li><b>CacheSize<\/b>: Anzahl Datens&auml;tze, die lokal zwischengespeichert werden.<\/li>\n<li><b>Cancel\/CancelBatch<\/b>: Bricht aktuelle Operationen ab.<\/li>\n<li><b>Clone<\/b>: Erstellt eine Kopie des Recordsets.<\/li>\n<li><b>Close\/Open<\/b>: &Ouml;ffnet oder schlie&szlig;t das referenzierte Recordset.<\/li>\n<li><b>CursorLocation\/CursorType<\/b>: Steuerung der Navigation und Server-\/Clientverarbeitung.<\/li>\n<li><b>Delete<\/b>: Entfernt den aktuellen Datensatz.<\/li>\n<li><b>EditMode<\/b>: Zeigt an, ob und wie ein Datensatz bearbeitet wird.<\/li>\n<li><b>Filter\/Find\/Seek<\/b>: Filtert Datens&auml;tze oder sucht bestimmte Inhalte.<\/li>\n<li><b>Fields<\/b>: Auflistung aller Felder eines Datensatzes.<\/li>\n<li><b>GetRows\/GetString<\/b>: Exportiert Daten als Array oder String.<\/li>\n<li><b>LockType<\/b>: Steuerung der Sperrmechanismen bei Bearbeitung.<\/li>\n<li><b>MoveFirst<\/b>: Navigiert zum ersten Datensatz im Recordset.<\/li>\n<li><b>MoveLast<\/b>: Navigiert zum letzten Datensatz im Recordset.<\/li>\n<li><b>MoveNext<\/b>: Navigiert zum n&auml;chsten Datensatz.<\/li>\n<li><b>MovePrevious<\/b>: Navigiert zum vorherigen Datensatz.<\/li>\n<li><b>Move: <\/b>Navigiert um eine bestimmte Anzahl Recordsets vor oder zur&uuml;ck.<\/li>\n<li><b>NextRecordset<\/b>: Weiteres Recordset aus einem Stapel.<\/li>\n<li><b>PageCount\/PageSize<\/b>: Paginierung gro&szlig;er Datenmengen.<\/li>\n<li><b>Properties<\/b>: Auflistung aller Eigenschaften.<\/li>\n<li><b>RecordCount<\/b>: Anzahl der Datens&auml;tze.<\/li>\n<li><b>Requery\/Resync<\/b>: Aktualisiert das Recordset mit aktuellen Daten.<\/li>\n<li><b>Save<\/b>: Speichert das Recordset als Datei.<\/li>\n<li><b>Sort<\/b>: Sortiert die Datens&auml;tze.<\/li>\n<li><b>Source\/State\/Status<\/b>: Informationsfelder zum Recordset.<\/li>\n<li><b>StayInSync\/Supports<\/b>: Steuerung von Synchronisierung und unterst&uuml;tzten Features.<\/li>\n<li><b>UpdateBatch<\/b>: Speichert mehrere &Auml;nderungen gesammelt.<\/li>\n<\/ul>\n<h2>Detaillierte Beschreibung aller Funktionen<\/h2>\n<p>Im folgenden Abschnitt erl&auml;utern wir jede dieser Methoden und Eigenschaften einzeln.<\/p>\n<p>Wir geben Anwendungsbeispiele, zeigen Einsatzm&ouml;glichkeiten in VBA und gehen auf Spezialf&auml;lle ein.<\/p>\n<h2>Beispielumgebung<\/h2>\n<p>In diesem Artikel verwenden wir durchg&auml;ngig die Tabelle <b>tblKunden<\/b> mit den Feldern <b>KundenID<\/b>, <b>Nachname<\/b>, <b>Vorname<\/b> und <b>Ort<\/b>. Die Datenbank enth&auml;lt f&uuml;nf Testdatens&auml;tze mit Namen und Orten.<\/p>\n<p>Diese erstellen wir mit einer eigenen Prozedur (siehe Listing 1).<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>TabelleKundenErstellenUndF&uuml;llen()\r\n     <span style=\"color:blue;\">Dim <\/span>cnn<span style=\"color:blue;\"> As <\/span>ADODB.Connection\r\n     <span style=\"color:blue;\">Dim <\/span>strPfad<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strSQL<span style=\"color:blue;\"> As String<\/span>\r\n     '' Verbindung zur aktuellen Access-Datenbank herstellen\r\n     <span style=\"color:blue;\">Set<\/span> cnn = <span style=\"color:blue;\">New<\/span> ADODB.Connection\r\n     strPfad = CurrentProject.FullName\r\n     cnn.ConnectionString = \"Provider=Microsoft.ACE.OLEDB.12.0;\" & _\r\n                            \"Data Source=\" & strPfad & \";\" & _\r\n                            \"Persist Security Info=False;\"\r\n     cnn.Open\r\n     '' Bestehende Tabelle l&ouml;schen (falls vorhanden)\r\n     On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n     cnn.Execute \"DROP TABLE tblKunden\"\r\n     <span style=\"color:blue;\">On Error GoTo<\/span> 0\r\n     '' Tabelle erstellen\r\n     strSQL = \"CREATE TABLE tblKunden (\" & _\r\n              \"KundenID AUTOINCREMENT PRIMARY KEY, \" & _\r\n              \"Nachname TEXT(50), \" & _\r\n              \"Vorname TEXT(50), \" & _\r\n              \"Ort TEXT(50))\"\r\n     cnn.Execute strSQL\r\n     '' Beispieldatens&auml;tze einf&uuml;gen\r\n     cnn.Execute \"INSERT INTO tblKunden (Nachname, Vorname, Ort) VALUES (''M&uuml;ller'', ''Anna'', ''Berlin'')\"\r\n     cnn.Execute \"INSERT INTO tblKunden (Nachname, Vorname, Ort) VALUES (''Schmidt'', ''Peter'', ''Hamburg'')\"\r\n     cnn.Execute \"INSERT INTO tblKunden (Nachname, Vorname, Ort) VALUES (''Lehmann'', ''Julia'', ''M&uuml;nchen'')\"\r\n     cnn.Execute \"INSERT INTO tblKunden (Nachname, Vorname, Ort) VALUES (''Weber'', ''Klaus'', ''Stuttgart'')\"\r\n     cnn.Execute \"INSERT INTO tblKunden (Nachname, Vorname, Ort) VALUES (''Klein'', ''Maria'', ''D&uuml;sseldorf'')\"\r\n     '' Verbindung schlie&szlig;en\r\n     cnn.Close\r\n     <span style=\"color:blue;\">Set<\/span> cnn = Nothing\r\n     <span style=\"color:blue;\">MsgBox<\/span> \"Tabelle ''tblKunden'' wurde erstellt und mit Daten gef&uuml;llt.\", vbInformation\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Erstellen einer Tabelle mit Beispieldaten<\/span><\/b><\/p>\n<h2>Verbindung und Recordset &ouml;ffnen<\/h2>\n<p>Alle folgenden Beispiele beginnen mit dem vollst&auml;ndigen Aufbau einer ADODB-Verbindung zu einer Access-Datenbank und dem &Ouml;ffnen eines Recordsets auf Basis der Tabelle <b>tblKunden<\/b>.<\/p>\n<p>Ein Beispiel hierzu sehen wir in Listing 2. Hier erstellen wir ein <b>Connection<\/b>-Objekt und f&uuml;llen es mit einem Verweis auf die Connection der aktuellen Access-Datenbank (<b>CurrentProject.Connection<\/b>).<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>KundenRecordsetOeffnen()\r\n     <span style=\"color:blue;\">Dim <\/span>cnn<span style=\"color:blue;\"> As <\/span>ADODB.Connection\r\n     <span style=\"color:blue;\">Dim <\/span>rst<span style=\"color:blue;\"> As <\/span>ADODB.Recordset\r\n     <span style=\"color:blue;\">Set<\/span> cnn = CurrentProject.Connection\r\n     <span style=\"color:blue;\">Set<\/span> rst = <span style=\"color:blue;\">New<\/span> ADODB.Recordset\r\n     rst.Open \"SELECT * FROM tblKunden\", cnn, adOpenKeyset, adLockOptimistic\r\n     Do Until rst.EOF\r\n         <span style=\"color:blue;\">Debug.Print<\/span> rst!Vorname & \" \" & rst!Nachname\r\n         rst.Move<span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n     rst.Close\r\n     <span style=\"color:blue;\">Set<\/span> rst = Nothing\r\n     cnn.Close\r\n     <span style=\"color:blue;\">Set<\/span> cnn = Nothing\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 2: Prozedur zum Durchlaufen von Kundendatens&auml;tzen<\/span><\/b><\/p>\n<p>Danach erstellen und &ouml;ffnen wir ein Recordset auf Basis der Tabelle <b>tblKunden<\/b>. Die Details beschreiben wir sp&auml;ter. Schlie&szlig;lich durchlaufen wir in einer <b>Do While<\/b>-Schleife alle Datens&auml;tze dieses Recordsets.<\/p>\n<h2>EOF und BOF<\/h2>\n<p>Die Eigenschaften <b>EOF<\/b> (End of File) und <b>BOF<\/b> (Beginning of File) zeigen an, ob sich der Datensatzzeiger am Ende oder vor dem Anfang des Recordsets befindet.<\/p>\n<p>Beide Eigenschaften sind vom Typ <b>Boolean<\/b> und spielen eine zentrale Rolle bei der sicheren Navigation durch Daten.<\/p>\n<p>Insbesondere bei leeren Recordsets ist es wichtig, beide Eigenschaften zu pr&uuml;fen: Ist <b>EOF<\/b> und <b>BOF<\/b> gleichzeitig <b>True<\/b>, enth&auml;lt das Recordset keine Datens&auml;tze &#8211; weder vorne noch hinten<\/p>\n<p>In Listing 3 erstellen wir ein Recordset, das keine Datens&auml;tze enth&auml;lt, da das Kriterium (1=2) f&uuml;r keinen Datensatz erf&uuml;llt wird.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>KundenDurchlaufen()\r\n     <span style=\"color:blue;\">Dim <\/span>cnn<span style=\"color:blue;\"> As <\/span>ADODB.Connection\r\n     <span style=\"color:blue;\">Dim <\/span>rst<span style=\"color:blue;\"> As <\/span>ADODB.Recordset\r\n     <span style=\"color:blue;\">Set<\/span> cnn = CurrentProject.Connection\r\n     <span style=\"color:blue;\">Set<\/span> rst = <span style=\"color:blue;\">New<\/span> ADODB.Recordset\r\n     rst.Open \"SELECT * FROM tblKunden WHERE 1 = 2\", cnn, adOpenKeyset, adLockOptimistic\r\n     <span style=\"color:blue;\">If <\/span>rst.BOF And rst.EOF<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">MsgBox<\/span> \"Keine Datens&auml;tze vorhanden.\"\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         ''Operationen f&uuml;r vorhandene Datens&auml;tze\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     rst.Close\r\n     <span style=\"color:blue;\">Set<\/span> rst = Nothing\r\n     cnn.Close\r\n     <span style=\"color:blue;\">Set<\/span> cnn = Nothing\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 3: Pr&uuml;fen, ob sich der Datensatzzeiger vor oder hinter dem ersten oder letzten Datensatz befindet.<\/span><\/b><\/p>\n<p>Hier pr&uuml;fen wir, ob <b>rst.EOF <\/b>und <b>rst.BOF <\/b>gleichzeitig <b>True <\/b>liefern. In diesem Fall gibt es keine Datens&auml;tze.<\/p>\n<p>In einem weiteren Beispiel nutzen wir <b>rst.EOF <\/b>bei einer Suche. Wir suchen mit der <b>Find<\/b>-Methode nach einem Datensatz mit einem bestimmten Kriterium. Nach der Suche pr&uuml;fen wir, ob <b>rst.EOF <\/b>wahr ist. In diesem Fall wird die Suche als erfolglos gemeldet, anderenfalls wird der gefundene Datensatz ausgegeben:<\/p>\n<pre>...\r\nrst.Find \"Nachname = ''Meyer''\"\r\n    \r\n<span style=\"color:blue;\">If <\/span>rst.EOF<span style=\"color:blue;\"> Then<\/span>\r\n     <span style=\"color:blue;\">MsgBox<\/span> \"Kunde ''Meyer'' wurde nicht gefunden.\", _\r\n         vbInformation\r\n<span style=\"color:blue;\">Else<\/span>\r\n     <span style=\"color:blue;\">MsgBox<\/span> \"Kunde gefunden: \" & rst!Vorname & \" \" _\r\n         & rst!Nachname\r\n<span style=\"color:blue;\">End If<\/span>\r\n...<\/pre>\n<h2>Auf Felder zugreifen mit Fields<\/h2>\n<p>&Uuml;ber die <b>Fields<\/b>-Auflistung kannst Du auf einzelne Spalten eines Datensatzes zugreifen. Du kannst sowohl den Feldnamen als auch den Index (beginnend bei <b>0<\/b>) verwenden.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>FeldZugriffBeispiel()\r\n...\r\n     rst.MoveFirst\r\n     <span style=\"color:blue;\">Debug.Print<\/span> rst.Fields(\"Vorname\").Value\r\n     <span style=\"color:blue;\">Debug.Print<\/span> rst.Fields(2).Value\r\n...\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>EditMode<\/h2>\n<p>Mit <b>EditMode<\/b> l&auml;sst sich abfragen, ob das Recordset aktuell bearbeitet wird.<\/p>\n<p>Diese Information ist besonders bei formulargebundenen oder interaktiv bearbeiteten Recordsets n&uuml;tzlich.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>BearbeitungsstatusPruefen()\r\n     ...\r\n     rst.Open \"SELECT * FROM tblKunden\", cnn, _\r\n         adOpenKeyset, adLockOptimistic\r\n     rst.MoveFirst\r\n     rst!Ort = \"Leipzig\"\r\n     <span style=\"color:blue;\">If <\/span>rst.EditMode = adEditInProgress<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">Debug.Print<\/span> \"Bearbeitung aktiv.\"\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     rst.Update\r\n     ...    \r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Datens&auml;tze hinzuf&uuml;gen mit AddNew\/Update<\/h2>\n<p>Neue Datens&auml;tze lassen sich mit <b>AddNew<\/b> erstellen. Anschlie&szlig;end musst Du mit <b>Update<\/b> speichern. Hier ein vollst&auml;ndiges Beispiel:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>KundeHinzufuegen()\r\n     ...\r\n     rst.Add<span style=\"color:blue;\">New<\/span>\r\n     rst!Vorname = \"Lisa\"\r\n     rst!Nachname = \"Neumann\"\r\n     rst!Ort = \"Hannover\"\r\n     rst.Update\r\n     ...\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Datens&auml;tze l&ouml;schen mit Delete<\/h2>\n<p>Um einen Datensatz zu l&ouml;schen, positionierst Du das Recordset auf den gew&uuml;nschten Eintrag und rufst <b>Delete<\/b> auf. Die &Auml;nderung wird sofort &uuml;bernommen:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>KundeLoeschen()\r\n     ...\r\n     <span style=\"color:blue;\">Set<\/span> rst = <span style=\"color:blue;\">New<\/span> ADODB.Recordset\r\n     rst.Open \"SELECT * FROM tblKunden WHERE Nachname = \" _\r\n         \"''Neumann''\", cnn, adOpenKeyset, adLockOptimistic\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> rst.EOF<span style=\"color:blue;\"> Then<\/span>\r\n         rst.Delete\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     ...\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Recordset filtern mit Filter<\/h2>\n<p>Mit der Eigenschaft <b>Filter<\/b> kannst Du ein ge&ouml;ffnetes Recordset einschr&auml;nken. Der Filter wirkt sich auf die Navigation und Methoden wie <b>MoveNext<\/b> aus.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>KundenInBerlin()\r\n     ...\r\n     rst.Open \"SELECT * FROM tblKunden\", cnn, _\r\n         adOpenKeyset, adLockOptimistic\r\n     \r\n     rst.Filter = \"Ort = ''Leipzig''\"\r\n     Do Until rst.EOF\r\n         <span style=\"color:blue;\">Debug.Print<\/span> rst!Vorname & \" \" & rst!Nachname\r\n         rst.Move<span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n     ...\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Datensatz suchen mit Find<\/h2>\n<p>Mit <b>Find<\/b> l&auml;sst sich der erste passende Datensatz ermitteln. Die Suche erfolgt relativ zur aktuellen Position. Als Parameter geben wir ein Filterkriterium an, das genauso aussieht wie das f&uuml;r die <b>WHERE<\/b>-Condition:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>KundeFinden()\r\n     ...\r\n     rst.Find \"Nachname = ''Schmidt''\"\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> rst.EOF<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">Debug.Print<\/span> \"Gefunden: \" & rst!Vorname\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     ...\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Datens&auml;tze sortieren mit Sort<\/h2>\n<p>Mit <b>Sort<\/b> kannst Du die aktuelle Reihenfolge der Datens&auml;tze im Recordset &auml;ndern &#8211; ohne erneute Datenbankabfrage. Hier ist es allerdings erforderlich, dass wir explizit die Eigenschaft <b>CursorLocation <\/b>auf <b>adUseClient <\/b>einstellen. Bisher haben wir diese Einstellung nicht vorgenommen, weil dies nicht notwendig war. Mehr zu dieser Einstellung weiter unten.<\/p>\n<p>Im Beispiel rufen wir die <b>Sort<\/b>-Methode mit dem Parameter <b>Nachname DESC <\/b>auf. Dadurch sortieren wir absteigend nach den Nachnamen:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>KundenSortieren()\r\n     ...\r\n     <span style=\"color:blue;\">Set<\/span> rst = <span style=\"color:blue;\">New<\/span> ADODB.Recordset\r\n     rst.CursorLocation = adUseClient\r\n     rst.Open \"SELECT * FROM tblKunden\", cnn, adOpenKeyset, adLockOptimistic\r\n     \r\n     rst.Sort = \"Nachname DESC\"\r\n     rst.MoveFirst\r\n     Do Until rst.EOF\r\n         <span style=\"color:blue;\">Debug.Print<\/span> rst!Nachname\r\n         rst.Move<span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n     ---\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Wenn wir nicht nur nach einem Feld sortieren wollen, geben wir die Sortierkriterien durch Kommata getrennt an &#8211; genau wie in der <b>ORDER BY<\/b>-Klausel von SQL:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>KundenSortierenMehrereKriterien()\r\n     ...    \r\n     rst.Sort = \"Nachname DESC, Vorname ASC\"\r\n     rst.MoveFirst\r\n     Do Until rst.EOF\r\n         <span style=\"color:blue;\">Debug.Print<\/span> rst!Nachname, rst!Vorname\r\n         rst.Move<span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n     ...\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>GetRows<\/h2>\n<p>Mit <b>GetRows<\/b> lassen sich Datens&auml;tze blockweise als Array auslesen &#8211; effizient f&uuml;r Auswertungen oder &Uuml;bergabe an andere Routinen.<\/p>\n<p>Im folgenden Beispiel f&uuml;llen wir ein Array auf Basis der <b>GetRows<\/b>-Funktion des ADODB-Recordsets. Danach durchlaufen wir alle Zeilen und Spalten und geben diese im Direktbereich von Access aus:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>KundenAlsArray()\r\n     <span style=\"color:blue;\">Dim <\/span>arr<span style=\"color:blue;\"> As Variant<\/span>\r\n     ...\r\n     arr = rst.GetRows\r\n     For j = <span style=\"color:blue;\">LBound<\/span>(arr, 2) To <span style=\"color:blue;\">UBound<\/span>(arr, 2) ''Datens&auml;tze\r\n         For i = <span style=\"color:blue;\">LBound<\/span>(arr, 1) To <span style=\"color:blue;\">UBound<\/span>(arr, 1) ''Felder\r\n             <span style=\"color:blue;\">Debug.Print<\/span> arr(i, j);\r\n             <span style=\"color:blue;\">If <\/span>i &lt; <span style=\"color:blue;\">UBound<\/span>(arr, 1)<span style=\"color:blue;\"> Then<\/span> <span style=\"color:blue;\">Debug.Print<\/span> \" | \";\r\n         <span style=\"color:blue;\">Next<\/span> i\r\n         <span style=\"color:blue;\">Debug.Print<\/span>\r\n     <span style=\"color:blue;\">Next<\/span> j\r\n     ...\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Dies gibt die Daten wie folgt aus:<\/p>\n<pre>1  | M&uuml;ller | Anna | Leipzig\r\n  2  | Schmidt | Peter | Hamburg\r\n  3  | Lehmann | Julia | M&uuml;nchen\r\n  4  | Weber | Klaus | Stuttgart\r\n  5  | Klein | Maria | D&uuml;sseldorf\r\n  7  | Klein | Nina | D&uuml;sseldorf\r\n  8  | Klein | Andrea | D&uuml;sseldorf<\/pre>\n<h2>Navigieren mit MoveFirst, MoveLast, MoveNext und MovePrevious<\/h2>\n<p>Diese Methoden erm&ouml;glichen die Navigation im Recordset. <b>MoveFirst<\/b> springt zum ersten Datensatz, <b>MoveLast<\/b> zum letzten. <b>MoveNext<\/b> und <b>MovePrevious<\/b> bewegen den Zeiger jeweils vorw&auml;rts oder r&uuml;ckw&auml;rts.<\/p>\n<p>Das folgende Beispiel zeigt die verschiedenen Aufrufe:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>NavigationKunden()\r\n...\r\nrst.Open \"SELECT * FROM tblKunden\", cnn, _\r\n     adOpenStatic,  adLockReadOnly\r\nrst.MoveLast\r\n<span style=\"color:blue;\">Debug.Print<\/span> \"Letzter Kunde: \" & rst!Nachname\r\nrst.MoveFirst\r\n<span style=\"color:blue;\">Debug.Print<\/span> \"Erster Kunde: \" & rst!Nachname\r\nrst.Move<span style=\"color:blue;\">Next<\/span>\r\n<span style=\"color:blue;\">Debug.Print<\/span> \"Zweiter Kunde: \" & rst!Nachname\r\n...<\/pre>\n<h2>Datensatzzeiger beliebig verschieben mit Move<\/h2>\n<p>Die Methode <b>Move<\/b> erm&ouml;glicht es Dir, den Datensatzzeiger um eine beliebige Anzahl von Positionen vorw&auml;rts oder r&uuml;ckw&auml;rts zu verschieben. Der erste Parameter gibt an, wie viele Datens&auml;tze der Zeiger verschoben werden soll. Positive Zahlen bewegen den Zeiger vorw&auml;rts, negative r&uuml;ckw&auml;rts. Ein zweiter, optionaler Parameter gibt an, von welcher Startposition aus die Bewegung erfolgen soll, meistens wird dieser aber nicht verwendet.<\/p>\n<p>Beispiel:<\/p>\n<pre>rst.Move 3 \" Springt drei Datens&auml;tze vor\r\nrst.Move -1 \" Springt einen Datensatz zur&uuml;ck<\/pre>\n<h2>Properties<\/h2>\n<p>Das <b>Properties<\/b>-Objekt enth&auml;lt zus&auml;tzliche Informationen zum Recordset &#8211; etwa Provider-spezifische Einstellungen.<\/p>\n<p>Hier durchlaufen wir alle <b>Property<\/b>-Elemente in einer <b>Do While<\/b>-Schleife und geben jeweils den <b>Property<\/b>-Namen und den Wert im Direktbereich des VBA-Editors aus:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>EigenschaftenAuflisten()\r\n     ...\r\n     <span style=\"color:blue;\">Dim <\/span>prp<span style=\"color:blue;\"> As <\/span>ADODB.Property\r\n     For Each prp In rst.Properties\r\n         <span style=\"color:blue;\">Debug.Print<\/span> prp.Name & \": \" & prp.Value\r\n     <span style=\"color:blue;\">Next<\/span> prp\r\n     ...\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>RecordCount<\/h2>\n<p><b>RecordCount<\/b> liefert die Anzahl der Datens&auml;tze. Achtung: Bei Verwendung des Wertes <b>adOpenForwardOnly<\/b> f&uuml;r den dritten Parameter der <b>Open<\/b>-Methode ist diese Information erst nach vollst&auml;ndigem Durchlauf verf&uuml;gbar.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>KundenZaehlen()\r\n     ...    \r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"Kundenanzahl: \" & rst.RecordCount\r\n     ...\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Requery<\/h2>\n<p><b>Requery<\/b> l&auml;dt die Daten im Recordset neu &#8211; ideal nach &Auml;nderungen in der Datenquelle an anderer Stelle:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>KundenNeuLaden()\r\n     ...\r\n     rst.Requery\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"Neu geladen: \" & rst.RecordCount _\r\n         & \" Datens&auml;tze\"\r\n     ...\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Resync<\/h2>\n<p>Mit der Methode <b>Resync<\/b> kannst Du den Inhalt eines Recordsets aktualisieren &#8211; entweder vollst&auml;ndig oder selektiv.<\/p>\n<p>Das ist besonders n&uuml;tzlich, wenn sich die zugrunde liegenden Daten au&szlig;erhalb des Recordsets ge&auml;ndert haben, zum Beispiel durch parallele Bearbeitung oder nach einem Abgleich mit dem Server.<\/p>\n<p>Die Methode kann entweder f&uuml;r den aktuellen Datensatz oder das gesamte Recordset ausgef&uuml;hrt werden &#8211; je nach Parameterwahl:<\/p>\n<ul>\n<li><b>adAffectCurrent<\/b>: nur aktueller Datensatz<\/li>\n<li><b>adAffectGroup<\/b>: alle Datens&auml;tze mit gleichem Filter<\/li>\n<li><b>adAffectAll<\/b>: gesamtes Recordset<\/li>\n<\/ul>\n<p>Im folgenden Beispiel aktualisieren wir gezielt den aktuellen Datensatz, um &Auml;nderungen von au&szlig;en zu &uuml;bernehmen (zum Beispiel durch andere Benutzer):<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>AktuellenKundenResyncen()\r\n     ...\r\n     rst.CursorLocation = adUseClient\r\n     rst.Open \"SELECT * FROM tblKunden\", cnn, _\r\n         adOpenKeyset, adLockOptimistic\r\n     rst.MoveFirst\r\n     <span style=\"color:blue;\">MsgBox<\/span> \"Vor Resync: \" & rst!Ort\r\n     rst.Resync adAffectCurrent\r\n     <span style=\"color:blue;\">MsgBox<\/span> \"Nach Resync: \" & rst!Ort\r\n     ...\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Um alle Daten im Recordset auf den aktuellen Stand zu bringen, etwa nach &Auml;nderungen in der Tabelle durch andere Prozesse, verwendest Du:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>GesamtesRecordsetResyncen()\r\n     ...\r\n     rst.Resync adAffectAll\r\n     <span style=\"color:blue;\">MsgBox<\/span> \"Synchronisierung abgeschlossen.\"\r\n     ...\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>&Auml;nderungen auf einen Rutsch mit UpdateBatch<\/h2>\n<p>Die Methode <b>UpdateBatch<\/b> dient dazu, mehrere &Auml;nderungen im Recordset auf einmal an die zugrunde liegende Datenquelle zu &uuml;bergeben. Diese Technik ist besonders n&uuml;tzlich, wenn Du im Hintergrund mehrere <b>AddNew<\/b>-, <b>Update<\/b>&#8211; oder <b>Delete<\/b>-Operationen ausf&uuml;hrst und erst sp&auml;ter gesammelt speichern willst.<\/p>\n<p>Damit <b>UpdateBatch<\/b> verwendet werden kann, muss das Recordset im <b>BatchUpdate<\/b>-Modus ge&ouml;ffnet sein. Daf&uuml;r sind zwei Voraussetzungen notwendig:<\/p>\n<ul>\n<li><b>CursorLocation<\/b> = <b>adUseClient<\/b><\/li>\n<li><b>LockType<\/b> = <b>adLockBatchOptimistic<\/b><\/li>\n<\/ul>\n<p>Ein Typisches Einsatzszenario ist, dass Du ein Recordset vollst&auml;ndig in den Speicher l&auml;dst, &Auml;nderungen an mehreren Zeilen vornimmst und sie erst nach der Validierung gesammelt an die Datenbank &uuml;bergibst.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>KundenBatchSpeichern()\r\n     ...\r\n     rst.Open \"SELECT * FROM tblKunden\", cnn, _\r\n         adOpenStatic, adLockBatchOptimistic\r\n     rst.Add<span style=\"color:blue;\">New<\/span>\r\n     rst!Vorname = \"Anja\"\r\n     rst!Nachname = \"Kaiser\"\r\n     rst!Ort = \"Mainz\"\r\n     rst.Update\r\n     rst.Add<span style=\"color:blue;\">New<\/span>\r\n     rst!Vorname = \"Leon\"\r\n     rst!Nachname = \"Brandt\"\r\n     rst!Ort = \"Trier\"\r\n     rst.Update\r\n     rst.UpdateBatch\r\n     ...\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Der Vorteil von <b>UpdateBatch<\/b> liegt bei:<\/p>\n<ul>\n<li>Validierung von &Auml;nderungen vor dem Speichern<\/li>\n<li>M&ouml;glichst wenige Schreibzugriffe bei Netzwerkverbindungen<\/li>\n<li>Aktualisierung mehrerer logisch zusammenh&auml;ngender Datens&auml;tze<\/li>\n<\/ul>\n<p>Nach dem Aufruf von <b>UpdateBatch<\/b> kannst Du den <b>Status<\/b> jedes Datensatzes auswerten und gezielt Konflikte oder Fehler behandeln.<\/p>\n<h2>Speichern von Recordsets in eine Datei mit Save<\/h2>\n<p><b>Save<\/b> speichert ein Recordset in eine Datei, zum Beispiel als XML-Datei. Dies ist praktisch f&uuml;r Offline-Verarbeitung oder Datensicherung:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>KundenExportieren()\r\n...\r\n     rst.Save CurrentProject.Path & \"\\kunden.xml\", _\r\n         adPersistXML\r\n...\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>NextRecordset<\/h2>\n<p>Mit der Methode <b>NextRecordset<\/b> kannst Du innerhalb eines Recordsets zum n&auml;chsten Teilergebnis springen, also zum Beispiel, wenn ein SQL-Befehl mehrere <b>SELECT<\/b>-Anweisungen enth&auml;lt oder eine gespeicherte Prozedur mehrere Resultsets zur&uuml;ckgibt.<\/p>\n<p>Typische Einsatzszenarien:<\/p>\n<ul>\n<li>Mehrere <b>SELECT<\/b>-Anweisungen in einem einzigen SQL-Text<\/li>\n<li>R&uuml;ckgabe mehrerer Ergebnismengen aus einer gespeicherten Prozedur (SQL Server)<\/li>\n<li>Analyse von Systemabfragen oder kombinierten Reports<\/li>\n<\/ul>\n<p>Der erste <b>Open<\/b>-Aufruf liefert das erste Resultset. Danach wechselst Du mit <b>NextRecordset<\/b> auf das n&auml;chste &#8211; bis die Methode <b>Nothing<\/b> zur&uuml;ckgibt.<\/p>\n<p>Beispiel: Zwei <b>SELECT<\/b>-Anweisungen in einem Befehl lassen sich wie folgt verarbeiten.<\/p>\n<p>Voraussetzung daf&uuml;r ist allerdings, dass wir die Daten aus einem Provider beziehen, der mehrere Resultsets unterst&uuml;tzt. Bei Access ist das nicht der Fall, aber zum Beispiel bei SQL Server. Dann k&ouml;nnen wir eine solche Abfrage &ouml;ffnen und die Recordsets mit <b>NextRecordset <\/b>durchlaufen:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>MehrereRecordsets()\r\n     <span style=\"color:blue;\">Set<\/span> cnn = <span style=\"color:blue;\">New<\/span> ADODB.Connection\r\n     cnn.ConnectionString = \"...SQL Server-Connection...\"\r\n     cnn.Open    \r\n     <span style=\"color:blue;\">Set<\/span> rst = <span style=\"color:blue;\">New<\/span> ADODB.Recordset\r\n     rst.Open \"SELECT * FROM tblKunden;\"_\r\n         \"SELECT * FROM tblKunden WHERE Ort = \"K&ouml;ln''\", _\r\n         cnn, adOpenStatic, adLockReadOnly\r\n     \r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"Erstes Recordset: \" & rst.RecordCount\r\n     \r\n     <span style=\"color:blue;\">Set<\/span> rs2 = rst.NextRecordset\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> rs2 Is Nothing<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">Debug.Print<\/span> \"Zweites Recordset: \" _\r\n             & rs2.RecordCount\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     \r\n     rst.Close\r\n     rs2.Close\r\n     <span style=\"color:blue;\">Set<\/span> rst = Nothing\r\n     <span style=\"color:blue;\">Set<\/span> rs2 = Nothing\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Supports<\/h2>\n<p>Mit <b>Supports<\/b> pr&uuml;fst Du, ob ein bestimmtes Feature vom Recordset unterst&uuml;tzt wird.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>PruefeBatchUnterstuetzung()\r\n...\r\n     <span style=\"color:blue;\">If <\/span>rst.Supports(adUpdateBatch)<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">MsgBox<\/span> \"Batch Update wird unterst&uuml;tzt.\"\r\n     <span style=\"color:blue;\">End If<\/span>\r\n...\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Abgefragt werden k&ouml;nnen unter anderem folgende Konstanten:<\/p>\n<ul>\n<li><b>adAddNew<\/b>: Gibt an, ob das Recordset das Hinzuf&uuml;gen neuer Datens&auml;tze &uuml;ber <b>AddNew<\/b>: unterst&uuml;tzt.<\/li>\n<li><b>adDelete<\/b>: Gibt an, ob Datens&auml;tze mit <b>Delete <\/b>entfernt werden k&ouml;nnen.<\/li>\n<li><b>adUpdate<\/b>: Zeigt an, ob &Auml;nderungen an Feldern mit <b>Update<\/b> gespeichert werden k&ouml;nnen.<\/li>\n<li><b>adBookmark<\/b>: Erm&ouml;glicht den Einsatz von Bookmarks zur Navigation und Speicherung von Positionen im Recordset.<\/li>\n<li><b>adSeek<\/b>: Gibt an, ob mit <b>Seek<\/b> ein Direktzugriff &uuml;ber indizierte Felder m&ouml;glich ist (nur mit bestimmten Providern wie Jet).<\/li>\n<li><b>adUpdateBatch<\/b>: Gibt an, ob Batch-&Auml;nderungen unterst&uuml;tzt werden &#8211; das hei&szlig;t, mehrere &Auml;nderungen werden gesammelt und gemeinsam &uuml;ber <b>UpdateBatch<\/b> gespeichert.<\/li>\n<\/ul>\n<h2>StayInSync<\/h2>\n<p><b>StayInSync<\/b> ist nur bei hierarchischen Recordsets relevant &#8211; zum Beispiel bei DataShape-Providern. In Access spielt es keine praktische Rolle.<\/p>\n<h2>State<\/h2>\n<p>Die Eigenschaft <b>State<\/b> gibt den aktuellen Verbindungs- oder Objektstatus eines ADODB-Objekts zur&uuml;ck. Dabei handelt es sich um eine Bitmaske (Integerwert), der je nach Objekt verschiedene Status anzeigt. Bei Recordsets und Verbindungen wird so zum Beispiel unterschieden, ob das Objekt ge&ouml;ffnet ist oder nicht.<\/p>\n<p>F&uuml;r Recordsets liefert <b>State<\/b> typischerweise einen der folgenden Werte:<\/p>\n<ul>\n<li><b>adStateClosed<\/b> (0): Das Recordset ist geschlossen.<\/li>\n<li><b>adStateOpen<\/b> (1): Das Recordset ist ge&ouml;ffnet.<\/li>\n<li><b>adStateConnecting<\/b> (2): Wird verwendet bei Verbindungen &#8211; das Objekt stellt gerade eine Verbindung her.<\/li>\n<li><b>adStateExecuting<\/b> (4): Es wird gerade ein Befehl ausgef&uuml;hrt.<\/li>\n<li><b>adStateFetching<\/b> (8): Daten werden gerade abgerufen.<\/li>\n<\/ul>\n<p>Da es sich um eine Bitmaske handelt, k&ouml;nnen mehrere Zust&auml;nde gleichzeitig zutreffen &#8211; zum Beispiel <b>adStateOpen + adStateFetching<\/b>.<\/p>\n<p>Das folgende Beispiel gibt den <b>State<\/b>-Wert f&uuml;r verschiedene Zust&auml;nde des Recordsets zur&uuml;ck:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>RecordsetStatusTest()\r\n     ...\r\n     <span style=\"color:blue;\">Set<\/span> rst = <span style=\"color:blue;\">New<\/span> ADODB.Recordset\r\n     \r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"Nach Initialisierung: \" & rst.State\r\n     \r\n     rst.Open \"SELECT * FROM tblKunden\", cnn, _\r\n         adOpenStatic, adLockReadOnly\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"Nach &Ouml;ffnen: \" & rst.State\r\n     \r\n     rst.Close\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"Nach Schlie&szlig;en: \" & rst.State\r\n     ...\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Status<\/h2>\n<p>Die Eigenschaft <b>Status<\/b> liefert f&uuml;r jeden Datensatz eines Recordsets Informationen dar&uuml;ber, ob &Auml;nderungen erfolgt sind, Konflikte vorliegen oder der Datensatz gel&ouml;scht wurde. Diese Eigenschaft ist besonders im Zusammenhang mit <b>Batch-Updates<\/b> wichtig.<\/p>\n<p>Die m&ouml;glichen Werte sind Bitmasken und lassen sich daher auch kombinieren:<\/p>\n<ul>\n<li><b>adRecOK<\/b> (<b>0<\/b>): Kein Fehler, Datensatz ist g&uuml;ltig.<\/li>\n<li><b>adRecNew<\/b> (<b>1<\/b>): Datensatz wurde hinzugef&uuml;gt.<\/li>\n<li><b>adRecModified<\/b> (<b>2<\/b>): Datensatz wurde ge&auml;ndert.<\/li>\n<li><b>adRecDeleted<\/b> (<b>4<\/b>): Datensatz wurde gel&ouml;scht.<\/li>\n<li><b>adRecUnmodified<\/b> (<b>8<\/b>): Keine &Auml;nderungen.<\/li>\n<li><b>adRecInvalid<\/b> <b>(16<\/b>): Ung&uuml;ltiger Datensatz (z. B. nach Fehler).<\/li>\n<li><b>adRecMultipleChanges<\/b> (<b>64<\/b>): Mehrere &Auml;nderungen (z. B. Konflikt).<\/li>\n<li><b>adRecPendingChanges<\/b> (<b>128<\/b>): &Auml;nderungen stehen noch aus.<\/li>\n<li><b>adRecCanceled<\/b> (<b>256<\/b>): &Auml;nderung wurde verworfen.<\/li>\n<li><b>adRecCantRelease<\/b> (<b>1024<\/b>): Kann nicht freigegeben werden.<\/li>\n<li><b>adRecConcurrencyViolation<\/b> (<b>2048<\/b>): Zeitgleich bearbeitet.<\/li>\n<li><b>adRecIntegrityViolation<\/b> (<b>4096<\/b>): Versto&szlig; gegen Integrit&auml;t.<\/li>\n<li><b>adRecSchemaViolation<\/b> (<b>8192<\/b>): Schemafehler.<\/li>\n<li><b>adRecDBDeleted<\/b> (262144): Datensatz wurde in DB gel&ouml;scht.<\/li>\n<\/ul>\n<p>Im folgenden Beispiel werden mehrere &Auml;nderungen vorgenommen und anschlie&szlig;end alle Statuswerte der betroffenen Datens&auml;tze analysiert:<\/p>\n<p>Das folgende Beispiel demonstriert, wie neue, ge&auml;nderte und gel&ouml;schte Datens&auml;tze im Recordset erkannt und ausgegeben werden k&ouml;nnen. Voraussetzung ist die Verwendung eines clientseitigen Cursors in Verbindung mit <b>adLockBatchOptimistic<\/b>.<\/p>\n<p>Im Beispiel wird ein Kunde neu angelegt, ein anderer aktualisiert und ein dritter gel&ouml;scht. Einen vierten belassen wir unver&auml;ndert. Nach jeder Aktion wird der aktuelle Status ausgeben:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>GezielteStatusAuswertung()\r\n     <span style=\"color:blue;\">Set<\/span> rst = <span style=\"color:blue;\">New<\/span> ADODB.Recordset\r\n     rst.CursorLocation = adUseClient\r\n     rst.Open \"SELECT * FROM tblKunden\", cnn, _\r\n         adOpenStatic, adLockBatchOptimistic\r\n     ''Neuen Datensatz hinzuf&uuml;gen\r\n     rst.Add<span style=\"color:blue;\">New<\/span>\r\n     rst!Vorname = \"Tom\"\r\n     rst!Nachname = \"Mustermann\"\r\n     rst!Ort = \"Berlin\"\r\n     <span style=\"color:blue;\">Debug.Print<\/span> rst.Status\r\n     rst.Update\r\n     ''Einen bestehenden Datensatz &auml;ndern\r\n     rst.MoveFirst\r\n     rst!Ort = \"Hamburg\"\r\n     <span style=\"color:blue;\">Debug.Print<\/span> rst.Status\r\n     rst.Update\r\n     ''Einen anderen Datensatz l&ouml;schen\r\n     rst.Move<span style=\"color:blue;\">Next<\/span>\r\n     rst.Delete\r\n     <span style=\"color:blue;\">Debug.Print<\/span> rst.Status\r\n     ''Den n&auml;chsten Datensatz unver&auml;ndert lassen\r\n     rst.Move<span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Debug.Print<\/span> rst.Status\r\n     rst.Close\r\n     <span style=\"color:blue;\">Set<\/span> rst = Nothing\r\n     cnn.Close\r\n     <span style=\"color:blue;\">Set<\/span> cnn = Nothing\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>StayInSync<\/h2>\n<p><b>StayInSync<\/b> ist nur bei hierarchischen Recordsets relevant &#8211; zum Beispiel bei DataShape-Providern. In Access spielt es keine praktische Rolle.<\/p>\n<h2>Supports<\/h2>\n<p>Mit der Methode <b>Supports<\/b> kannst Du pr&uuml;fen, ob ein bestimmtes Feature im aktuellen Recordset unterst&uuml;tzt wird. Dies ist besonders wichtig, um zur Laufzeit dynamisch auf unterschiedliche Cursor- oder Lock-Typen reagieren zu k&ouml;nnen &#8211; zum Beispiel, ob <b>Seek<\/b> oder <b>UpdateBatch<\/b> verf&uuml;gbar ist.<\/p>\n<p>Die m&ouml;glichen Konstanten lauten:<\/p>\n<ul>\n<li><b>adAddNew<\/b>: K&ouml;nnen neue Datens&auml;tze hinzugef&uuml;gt werden (zum Beispiel mit <b>AddNew<\/b>)?<\/li>\n<li><b>adDelete<\/b>: K&ouml;nnen Datens&auml;tze gel&ouml;scht werden?<\/li>\n<li><b>adUpdate<\/b>: Werden &Auml;nderungen an bestehenden Datens&auml;tzen werden unterst&uuml;tzt?<\/li>\n<li><b>adBookmark<\/b>: K&ouml;nnen Datens&auml;tze mit <b>Bookmark<\/b> referenziert werden?<\/li>\n<li><b>adSeek<\/b>: Ist die <b>Seek<\/b>-Methode ist verf&uuml;gbar? Hier ist ein Index erforderlich.<\/li>\n<li><b>adUpdateBatch<\/b>: Liegt Unterst&uuml;tzung f&uuml;r <b>Batch-Updates<\/b> (zum Beispiel bei clientseitigen Recordsets) vor?<\/li>\n<\/ul>\n<p>In Listing 4 ist ein Beispiel, um die verschiedenen Funktionen abzufragen.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>RecordsetFaehigkeitenTesten()\r\n     <span style=\"color:blue;\">Dim <\/span>cnn<span style=\"color:blue;\"> As <\/span>ADODB.Connection\r\n     <span style=\"color:blue;\">Dim <\/span>rst<span style=\"color:blue;\"> As <\/span>ADODB.Recordset\r\n     <span style=\"color:blue;\">Set<\/span> cnn = CurrentProject.Connection\r\n     <span style=\"color:blue;\">Set<\/span> rst = <span style=\"color:blue;\">New<\/span> ADODB.Recordset\r\n     rst.Open \"SELECT * FROM tblKunden\", cnn, adOpenKeyset, adLockOptimistic\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"Unterst&uuml;tzt AddNew: \" & rst.Supports(adAdd<span style=\"color:blue;\">New<\/span>)\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"Unterst&uuml;tzt Delete: \" & rst.Supports(adDelete)\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"Unterst&uuml;tzt Update: \" & rst.Supports(adUpdate)\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"Unterst&uuml;tzt UpdateBatch: \" & rst.Supports(adUpdateBatch)\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"Unterst&uuml;tzt Bookmark: \" & rst.Supports(adBookmark)\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"Unterst&uuml;tzt Seek: \" & rst.Supports(adSeek)\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 4: Verschiedene Funktionen des Recordsets pr&uuml;fen<\/span><\/b><\/p>\n<p>Hinweis: Viele dieser Funktionen stehen nur bei bestimmten Kombinationen aus <b>CursorType<\/b>, <b>LockType<\/b> und <b>CursorLocation<\/b> zur Verf&uuml;gung. So funktioniert <b>Seek<\/b> zum Beispiel nur bei <b>adOpenKeyset<\/b> mit <b>Index<\/b> und <b>adUseServer<\/b>.<\/p>\n<h2>Recordset-Ereignisse<\/h2>\n<p>ADO bietet eine Reihe von Ereignissen, mit denen Du die Vorg&auml;nge innerhalb eines Recordsets &uuml;berwachen kannst. Diese Ereignisse sind vor allem dann hilfreich, wenn Du umfangreiche Datenmanipulationen durchf&uuml;hrst oder auf bestimmte &Auml;nderungen im Recordset reagieren willst &#8211; etwa bevor ein Datensatz ge&auml;ndert oder gel&ouml;scht wird.<\/p>\n<p>Um Recordset-Ereignisse in VBA nutzen zu k&ouml;nnen, musst Du das Recordset-Objekt mit <b>WithEvents<\/b> deklarieren. Dies ist nur in einem Klassenmodul m&ouml;glich. Es gibt die folgenden Ereignisse f&uuml;r ein <b>Recordset<\/b>-Objekt:<\/p>\n<ul>\n<li><b>WillChangeField<\/b>: Vor der &Auml;nderung eines Feldes<\/li>\n<li><b>FieldChangeComplete<\/b>: Nach der &Auml;nderung eines Feldes<\/li>\n<li><b>WillChangeRecord<\/b>: Vor der &Auml;nderung eines ganzen Datensatzes<\/li>\n<li><b>RecordChangeComplete<\/b>: Nach der &Auml;nderung eines Datensatzes<\/li>\n<li><b>WillChangeRecordset<\/b>: Vor grundlegenden Recordset-Aktionen (zum Beispiel zum &Ouml;ffnen oder Schlie&szlig;en)<\/li>\n<li><b>RecordsetChangeComplete<\/b>: Nach grundlegenden Recordset-Aktionen<\/li>\n<li><b>MoveComplete<\/b>: Nach dem Positionswechsel im Recordset<\/li>\n<li><b>EndOfRecordset<\/b>: Beim Erreichen des Endes<\/li>\n<li><b>FetchProgress<\/b>: W&auml;hrend des Abrufs von Daten (bei asynchronen Recordsets)<\/li>\n<li><b>FetchComplete<\/b>: Nach Abschluss des Abrufs<\/li>\n<\/ul>\n<h2>Beispiel: Alle Ereignisse beobachten<\/h2>\n<p>Das folgende Beispiel zeigt ein Klassenmodul mit dem Namen <b>clsRecordsetEvents<\/b>, in dem alle Ereignisse eines Recordsets ausgegeben werden. Das Hauptmodul instanziert dieses Objekt und l&ouml;st typische Aktionen aus.<\/p>\n<p>Als Erstes erstellen legen wir ein neues Klassenmodul namens <b>clsRecordsetEvents <\/b>an. In diesem f&uuml;gen wir die Variable f&uuml;r das Recordset hinzu. Au&szlig;erdem legen wir eine <b>Property Set<\/b>-Prozedur f&uuml;r diese Variable an, mit der die Klasse einen Verweis auf das zu beobachtende Recordset entgegennehmen kann:<\/p>\n<pre><span style=\"color:blue;\">Private <\/span>WithEvents rst<span style=\"color:blue;\"> As <\/span>ADODB.Recordset\r\n<span style=\"color:blue;\">Public Property <span style=\"color:blue;\">Set<\/span> <\/span>rst(rstInput<span style=\"color:blue;\"> As <\/span>ADODB.Recordset)\r\n     <span style=\"color:blue;\">Set<\/span> rst = rstInput\r\n<span style=\"color:blue;\">End Property<\/span><\/pre>\n<p>Danach f&uuml;gen wir alle Ereignisse f&uuml;r diese Objektvariable hinzu. Damit das m&ouml;glich ist, haben wir die Variable mit dem Schl&uuml;sselwort <b>WithEvents <\/b>deklariert. Wir k&ouml;nnen nun im VBA-Editor im linken Kombinationsfeld den Eintrag <b>rst<\/b> ausw&auml;hlen und finden dann im rechten Kombinationsfeld alle verf&uuml;gbaren Ereignisse vor (siehe Bild 1).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2025_02\/pic_471_002.png\" alt=\"Hinzuf&uuml;gen eines Ereignisses f&uuml;r die Variable rst\" width=\"649,627\" height=\"287,3187\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Hinzuf&uuml;gen eines Ereignisses f&uuml;r die Variable rst<\/span><\/b><\/p>\n<p>Diese legen wir nun nacheinander an. Dann f&uuml;llen wir sie mit jeweils einer <b>Debug.Print<\/b>-Anweisung, mit der wir den Namen des Ereignisses und die Parameter ausgeben, die das jeweilige Ereignis zur Verf&uuml;gung stellt. Insgesamt sieht dies wie in Listing 5 aus.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>rst_EndOfRecordset(fMoreData<span style=\"color:blue;\"> As Boolean<\/span>, adStatus<span style=\"color:blue;\"> As <\/span>ADODB.EventStatusEnum, ByVal pRecordset<span style=\"color:blue;\"> As <\/span>_\r\n        ADODB.Recordset)\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"EndOfRecordset | fMoreData: \" & fMoreData & \" | Status: \" & adStatus\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Private Sub <\/span>rst_FetchComplete(ByVal pError<span style=\"color:blue;\"> As <\/span>ADODB.Error, adStatus<span style=\"color:blue;\"> As <\/span>ADODB.EventStatusEnum, ByVal pRecordset _\r\n        <span style=\"color:blue;\"> As <\/span>ADODB.Recordset)\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"FetchComplete | Fehler: \" & IIf(pError Is Nothing, \"keiner\", pError.Description) & \" | Status: \" & adStatus\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Private Sub <\/span>rst_FetchProgress(ByVal Progress<span style=\"color:blue;\"> As Long<\/span>, ByVal MaxProgress<span style=\"color:blue;\"> As Long<\/span>, adStatus<span style=\"color:blue;\"> As <\/span>ADODB.EventStatusEnum, _\r\n         ByVal pRecordset<span style=\"color:blue;\"> As <\/span>ADODB.Recordset)\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"FetchProgress | Fortschritt: \" & Progress & \" \/ \" & MaxProgress & \" | Status: \" & adStatus\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Private Sub <\/span>rst_FieldChangeComplete(ByVal cFields<span style=\"color:blue;\"> As Long<\/span>, ByVal Fields<span style=\"color:blue;\"> As Variant<\/span>, ByVal pError<span style=\"color:blue;\"> As <\/span>ADODB.Error, _\r\n         adStatus<span style=\"color:blue;\"> As <\/span>ADODB.EventStatusEnum, ByVal pRecordset<span style=\"color:blue;\"> As <\/span>ADODB.Recordset)\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"FieldChangeComplete | Felder: \" & cFields & \" | Fehler: \" & GetError(pError) & \" | Status: \" & adStatus\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Private Sub <\/span>rst_MoveComplete(ByVal adReason<span style=\"color:blue;\"> As <\/span>ADODB.EventReasonEnum, ByVal pError<span style=\"color:blue;\"> As <\/span>ADODB.Error, adStatus<span style=\"color:blue;\"> As <\/span>_\r\n         ADODB.EventStatusEnum, ByVal pRecordset<span style=\"color:blue;\"> As <\/span>ADODB.Recordset)\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"MoveComplete | Reason: \" & adReason & \" | Fehler: \" & GetError(pError) & \" | Status: \" & adStatus\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Private Sub <\/span>rst_RecordChangeComplete(ByVal adReason<span style=\"color:blue;\"> As <\/span>ADODB.EventReasonEnum, ByVal cRecords<span style=\"color:blue;\"> As Long<\/span>, ByVal pError _\r\n        <span style=\"color:blue;\"> As <\/span>ADODB.Error, adStatus<span style=\"color:blue;\"> As <\/span>ADODB.EventStatusEnum, ByVal pRecordset<span style=\"color:blue;\"> As <\/span>ADODB.Recordset)\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"RecordChangeComplete | Reason: \" & adReason & \" | Records: \" & cRecords & \" | Fehler: \" _\r\n         & GetError(pError) & \" | Status: \" & adStatus\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Private Sub <\/span>rst_RecordsetChangeComplete(ByVal adReason<span style=\"color:blue;\"> As <\/span>ADODB.EventReasonEnum, ByVal pError<span style=\"color:blue;\"> As <\/span>ADODB.Error, _\r\n         adStatus<span style=\"color:blue;\"> As <\/span>ADODB.EventStatusEnum, ByVal pRecordset<span style=\"color:blue;\"> As <\/span>ADODB.Recordset)\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"RecordsetChangeComplete | Reason: \" & adReason & \" | Fehler: \" & GetError(pError) & \" | Status: \" _\r\n         & adStatus\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Private Sub <\/span>rst_WillChangeField(ByVal cFields<span style=\"color:blue;\"> As Long<\/span>, ByVal Fields<span style=\"color:blue;\"> As Variant<\/span>, adStatus<span style=\"color:blue;\"> As <\/span>ADODB.EventStatusEnum, _\r\n         ByVal pRecordset<span style=\"color:blue;\"> As <\/span>ADODB.Recordset)\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"WillChangeField | Felder: \" & cFields & \" | Status: \" & adStatus\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Private Sub <\/span>rst_WillChangeRecord(ByVal adReason<span style=\"color:blue;\"> As <\/span>ADODB.EventReasonEnum, ByVal cRecords<span style=\"color:blue;\"> As Long<\/span>, adStatus<span style=\"color:blue;\"> As <\/span>_\r\n         ADODB.EventStatusEnum, ByVal pRecordset<span style=\"color:blue;\"> As <\/span>ADODB.Recordset)\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"WillChangeRecord | Reason: \" & adReason & \" | Records: \" & cRecords & \" | Status: \" & adStatus\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 5: Erster Teil des Klassenmoduls zur Ausgabe der Ereignisse und ihrer Parameter<\/span><\/b><\/p>\n<p>Im zweiten Teil der Klasse aus Listing 6 sehen wir au&szlig;erdem noch die Hilfsfunktion <b>GetError<\/b>. Diese nimmt das Fehlerobjekt entgegen und pr&uuml;ft, ob es &uuml;berhaupt vorhanden ist. Wenn kein Fehler aufgetreten ist, wird das Objekt n&auml;mlich gar nicht erst mit dem Parameter <b>pError <\/b>mitgegeben. Deshalb pr&uuml;fen wir in der Funktion, ob <b>err<\/b> &uuml;berhaupt vorhanden ist (<b>Is Nothing<\/b>). Falls nicht, geben wir den Wert <b>Keiner <\/b>zur&uuml;ck. Dies dient nur der Ausgabe, im Praxiseinsatz w&uuml;rden wir hier einen <b>Boolean<\/b>-Wert nutzen. Falls das Objekt &uuml;bergeben wurde, lesen wir mit der Eigenschaft <b>Description <\/b>die Fehlermeldung aus und geben diese zur&uuml;ck.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>rst_WillChangeRecordset(ByVal adReason<span style=\"color:blue;\"> As <\/span>ADODB.EventReasonEnum, adStatus<span style=\"color:blue;\"> As <\/span>ADODB.EventStatusEnum, _\r\n         ByVal pRecordset<span style=\"color:blue;\"> As <\/span>ADODB.Recordset)\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"WillChangeRecordset | Reason: \" & adReason & \" | Status: \" & adStatus\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Private Sub <\/span>rst_WillMove(ByVal adReason<span style=\"color:blue;\"> As <\/span>ADODB.EventReasonEnum, adStatus<span style=\"color:blue;\"> As <\/span>ADODB.EventStatusEnum, _\r\n         ByVal pRecordset<span style=\"color:blue;\"> As <\/span>ADODB.Recordset)\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"WillMove | Reason: \" & adReason & \" | Status: \" & adStatus\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Private Function <\/span>GetError(err<span style=\"color:blue;\"> As <\/span>Error)\r\n     <span style=\"color:blue;\">Dim <\/span>strError<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">If <\/span>err Is Nothing<span style=\"color:blue;\"> Then<\/span>\r\n         strError = \"Keiner\"\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         strError = err.Description\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     GetError = strError\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 6: Zweiter Teil des Klassenmoduls zur Ausgabe der Ereignisse und ihrer Parameter<\/span><\/b><\/p>\n<p>Nun ben&ouml;tigen wir noch ein Beispiel f&uuml;r die Nutzung dieser Klasse, damit wir sehen, wann welches Ereignis ausgel&ouml;st wird. Dieses legen wir wie in Listing 7 an.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>StarteRecordsetMitEvents()\r\n     <span style=\"color:blue;\">Dim <\/span>cnn<span style=\"color:blue;\"> As <\/span>ADODB.Connection\r\n     <span style=\"color:blue;\">Dim <\/span>rst<span style=\"color:blue;\"> As <\/span>ADODB.Recordset\r\n     <span style=\"color:blue;\">Dim <\/span>objRecordsetEvents<span style=\"color:blue;\"> As <\/span>clsRecordsetEvents\r\n     <span style=\"color:blue;\">Set<\/span> cnn = CurrentProject.Connection\r\n     \r\n     <span style=\"color:blue;\">Set<\/span> objRecordsetEvents = <span style=\"color:blue;\">New<\/span> clsRecordsetEvents\r\n     <span style=\"color:blue;\">Set<\/span> rst = <span style=\"color:blue;\">New<\/span> ADODB.Recordset\r\n     <span style=\"color:blue;\">Set<\/span> objRecordsetEvents.Recordset = rst\r\n     rst.Open \"SELECT * FROM tblKunden\", cnn, adOpenKeyset, adLockOptimistic\r\n     \r\n     '' Aktion: Datensatz &auml;ndern\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"MoveFirst\"\r\n     rst.MoveFirst\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"Ort setzen\"\r\n     rst!Ort = \"Koblenz\"\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"Update\"\r\n     rst.Update\r\n     \r\n     '' Aktion: Datensatz hinzuf&uuml;gen\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"AddNew\"\r\n     rst.Add<span style=\"color:blue;\">New<\/span>\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"Vorname setzen\"\r\n     rst!Vorname = \"Lea\"\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"Nachname setzen\"\r\n     rst!Nachname = \"Schwarz\"\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"Ort setzen\"\r\n     rst!Ort = \"Heidelberg\"\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"Update\"\r\n     rst.Update\r\n     '' Aktion: Datensatz l&ouml;schen\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"MoveLast\"\r\n     rst.MoveLast\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"Delete\"\r\n     rst.Delete\r\n     \r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"Close\"\r\n     rst.Close\r\n     <span style=\"color:blue;\">Set<\/span> objRecordsetEvents = Nothing\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 7: Aufruf der Klasse zur Ausgabe der ausgel&ouml;sten Ereignisse<\/span><\/b><\/p>\n<p>Es deklariert wie in den vorherigen Beispielen das <b>Connection<\/b>&#8211; und das <b>Recordset<\/b>-Objekt und initialisiert zun&auml;chst die Connection. Au&szlig;erdem initialisieren wir auch ein neues Objekt auf Basis der Klasse <b>clsRecordsetEvents <\/b>und weisen es der Variablen <b>objRecordsetEvents <\/b>zu.<\/p>\n<p>Danach initialisieren wir unser <b>Recordset<\/b>-Objekt in <b>rst <\/b>und f&uuml;gen es im n&auml;chsten Schritt &uuml;ber die Eigenschaft <b>Recordset <\/b>der Klasse <b>clsRecordsetEvents <\/b>hinzu. Anschlie&szlig;end &ouml;ffnen wir das Recordset auf Basis der Tabelle <b>tblKunden<\/b>. Danach beginnt die Bearbeitung der Daten dieses Recordsets, den Du am besten im Einzelschrittmodus durchl&auml;ufst:<\/p>\n<ul>\n<li>Bereits der <b>Open<\/b>-Befehl l&ouml;st die beiden Ereignisse <b>WillMove <\/b>und <b>MoveComplete <\/b>aus.<\/li>\n<li>Das gleiche geschieht bei <b>MoveFirst<\/b>.<\/li>\n<li>Das Setzen des Feldes <b>Ort<\/b> und das Setzen der weiteren Felder l&ouml;st die Ereignisse <b>WillChangeRecord<\/b>, <b>WillChangeField<\/b>, <b>FieldChangeComplete <\/b>und <b>RecordChangeComplete <\/b>aus.<\/li>\n<li>Die <b>Update<\/b>-Methode l&ouml;st die Ereignisse <b>WillChangeRecord <\/b>und <b>RecordChangeComplete <\/b>aus.<\/li>\n<li>Die <b>AddNew<\/b>-Methode l&ouml;st die Ereignisse <b>WillMove<\/b>, <b>WillChangeRecord<\/b>, <b>RecordChangeComplete <\/b>und <b>MoveComplete <\/b>aus.<\/li>\n<li>Die <b>Delete<\/b>-Methode l&ouml;st das Ereignis <b>RecordChangeComplete <\/b>aus.<\/li>\n<li>Schlie&szlig;lich l&ouml;st die <b>Close<\/b>-Methode das Ereignis <b>RecordsetChangeComplete <\/b>aus.<\/li>\n<\/ul>\n<p>Die Ereignisse bieten einige praktische Anwendungszwecke, die wir hier nicht mehr im Detail abhandeln k&ouml;nnen.<\/p>\n<p>Dazu geh&ouml;ren zum Beispiel:<\/p>\n<ul>\n<li>Validierung vor &Auml;nderungen (<b>WillChangeField<\/b>, <b>WillChangeRecord<\/b>): Zum Beispiel zum Pr&uuml;fen von Feldern, bevor der Benutzer einen Wert speichert oder ob eine E-Mail-Adresse ein korrektes Format hat oder ob Pflichtfelder ausgef&uuml;llt sind. Hier kann abgebrochen werden, indem wir den Parameter <b>adStatus <\/b>auf <b>adStatusCancel <\/b>setzt.<\/li>\n<li>Automatisches Protokollieren (<b>FieldChangeComplete<\/b>, <b>RecordChangeComplete<\/b>): Zum Beispiel zum Festhalten von &Auml;nderungen in einem Audit-Trail  (Beispiel: Jeder &Auml;nderungsversuch wird in eine Log-Tabelle geschrieben &#8211; wer hat wann was ge&auml;ndert)<\/li>\n<li>Verhindern von L&ouml;schvorg&auml;ngen (<b>WillChangeRecord<\/b>): Wir k&ouml;nnen vor dem L&ouml;schen pr&uuml;fen, ob der Datensatz noch referenziert wird.Beispiel: Ein Kunde soll nicht gel&ouml;scht werden, wenn es noch offene Bestellungen gibt.<\/li>\n<li>Automatische Anpassungen\/Zusatzlogik (<b>RecordChangeComplete<\/b>): Wir k&ouml;nnen automatisch Felder nachf&uuml;llen oder abh&auml;ngige Werte anpassen. Beispiel: Nach &Auml;nderung des Landes automatisch den Standardwert f&uuml;r das Versandkostenfeld setzen.<\/li>\n<li>Navigation kontrollieren (<b>WillMove<\/b>, <b>MoveComplete<\/b>): Damit k&ouml;nnen wir die Navigation einschr&auml;nken oder verhindern. Beispiel: Warnung, wenn der Benutzer versucht, den Datensatz mit ungespeicherten &Auml;nderungen zu verlassen.<\/li>\n<li>Benutzerinteraktion (zum Beispiel f&uuml;r visuelles Feedback: Hier k&ouml;nnen wir Ereignisse nutzen, um Buttons zu aktivieren\/deaktivieren. So lie&szlig;e sich ein <b>Speichern<\/b>-Button erst aktivieren, wenn wirklich &Auml;nderungen vorliegen.<\/li>\n<li>Asynchrone Datenabrufe &uuml;berwachen (<b>FetchProgress<\/b>, <b>FetchComplete<\/b>): Geeignet zum Anzeigen des Fortschritts anzeigen bei sehr gro&szlig;en Abfragen. Beispiel: Fortschrittsbalken anzeigen, w&auml;hrend das Recordset viele Datens&auml;tze l&auml;dt (nur bei asynchronem Abruf relevant).<\/li>\n<\/ul>\n<h2>Zusammenfassung und Ausblick<\/h2>\n<p>Das Recordset-Objekt von ADODB bietet einige M&ouml;glichkeiten mehr als das der DAO-Bibliothek. Die Events sind das Sahneh&auml;ubchen. Wir werden in einem sp&auml;teren Artikel Beispiele vorstellen, wie sich diese praktisch nutzen lassen.<\/p>\n<h2>Downloads zu diesem Beitrag<\/h2>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>ADODB_DatenzugriffPerRecordset.accdb<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/7264AED5-2228-4950-B48D-242EAABC12BF\/vbe_471.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Recordsets sind zentrale Bestandteile beim Zugriff auf Daten mit ADODB. Sie bieten umfangreiche Funktionen zur Navigation, Bearbeitung, Filterung und Analyse von Daten. In diesem Artikel zeigen wir eine vollst&auml;ndige &Uuml;bersicht aller Eigenschaften und Methoden des Recordset-Objekts und erl&auml;utern diese jeweils ausf&uuml;hrlich mit praktischen Beispielen.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"footnotes":""},"categories":[66022025,662025,44000008],"tags":[],"yst_prominent_words":[],"class_list":["post-55000471","post","type-post","status-publish","format-standard","hentry","category-66022025","category-662025","category-Datenzugriffstechnik"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000471","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/comments?post=55000471"}],"version-history":[{"count":0,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000471\/revisions"}],"wp:attachment":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/media?parent=55000471"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/categories?post=55000471"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/tags?post=55000471"},{"taxonomy":"yst_prominent_words","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/yst_prominent_words?post=55000471"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}