{"id":55001074,"date":"2017-02-01T00:00:00","date_gmt":"2020-05-14T13:41:20","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=1074"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"RDBMSZugriff_per_VBA_Daten_bearbeiten","status":"publish","type":"post","link":"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_bearbeiten\/","title":{"rendered":"RDBMS-Zugriff per VBA: Daten bearbeiten"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg09.met.vgwort.de\/na\/5d8bee01e28e464c94fa206d927d26f6\" width=\"1\" height=\"1\" alt=\"\"><\/p>\n<p><b>Im Beitrag &#8222;RDBMS-Zugriff per VBA: Verbindungen&#8220; haben wir die Grundlage f&uuml;r den Zugriff auf SQL Server-Datenbanken geschaffen. Zudem zeigt der Beitrag &#8222;RDBMS-Zugriff per VBA: Daten abfragen&#8220;, wie Sie die Daten einer SQL Server-Datenbank ermitteln. Im vorliegenden Teil dieser Beitragsreihe erfahren Sie nun, wie Sie die Daten einer SQL Server-Datenbank bearbeiten.<\/b><\/p>\n<h2>Aktionsabfragen<\/h2>\n<p>Aktionsabfragen sind Abfragen, die Daten &auml;ndern &#8211; also L&ouml;sch-, Aktualisierungs- und An-f&uuml;ge-ab-fra-gen. In reinen Access-Datenbanken f&uuml;hren Sie solche Abfragen aus, indem Sie diese mit dem Abfrage-Entwurf erstellen und direkt ausf&uuml;hren oder per VBA aufrufen oder indem Sie die gew&uuml;nschte Abfrage als SQL-Ausdruck per Code zusammenstellen und dann mit der <b>Execute<\/b>-Methode des <b>Database<\/b>-Objekts ausf&uuml;hren. F&uuml;r SQL Server-Daten gibt es die folgenden Arten der Ausf&uuml;hrung:<\/p>\n<ul>\n<li>Erstellen einer Aktionsabfrage in Access, die sich auf die Daten einer per ODBC verkn&uuml;pften Tabelle des SQL Servers bezieht,<\/li>\n<li>Erstellen einer Pass-Through-Abfrage, welche die Aktionsabfrage enth&auml;lt und diese direkt an den SQL Server &uuml;bermittelt,<\/li>\n<li>Erstellen einer gespeicherten Prozedur, welche die Aktionsabfrage enth&auml;lt und die notwen-digen Parameter entgegen nimmt &#8211; also beispielsweise die ID eines zu l&ouml;schenden Daten-satzes -, und die &uuml;ber eine Pass-Through-Abfrage aufgerufen wird.<\/li>\n<\/ul>\n<p>Wenn es um die Performance geht, ist die erste Variante die langsamste, die zweite Version ist etwas schneller und die dritte Version &auml;ndert die Daten in der Regel am schnellsten. Aus diesem Grund schauen wir uns nachfolgend lediglich die zweite und die dritte Variante an.<\/p>\n<h2>Datensatz l&ouml;schen per SQL<\/h2>\n<p>Bei der ersten Variante legen Sie eine Pass-Through-Abfrage mit der auszuf&uuml;hrenden <b>DELETE<\/b>-Anweisung an (s. Bild 1).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_01\/pic_1074_001.png\" alt=\"Die neue PassThrough-Abfrage zum L&ouml;schen eines Datensatzes im SQL Server\" width=\"700\" height=\"274,2039\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Die neue PassThrough-Abfrage zum L&ouml;schen eines Datensatzes im SQL Server<\/span><\/b><\/p>\n<p>Dazu sind folgende Schritte n&ouml;tig:<\/p>\n<ul>\n<li>Erstellen einer neuen, leeren Abfrage und Schlie&szlig;en des Dialogs <b>Tabelle anzeigen<\/b><\/li>\n<li>Wechseln des Abfragetyps auf <b>Pass-Through<\/b><\/li>\n<li>Einstellen der Eigenschaft <b>ODBC-Verbindung <\/b>auf die gew&uuml;nschte Verbindungszeichen-fol-ge (hier <b>ODBC;DRIVER={SQL Server Native Client 11.0};SERVER=(localdb)\\MSSQLLocalDB;DATABASE=Suedsturm;Trusted_Connection=Yes<\/b>)<\/li>\n<li>Einstellen der Eigenschaft <b>Liefert Datens&auml;tze <\/b>auf <b>Nein<\/b><\/li>\n<li>Eintragen der <b>DELETE<\/b>-Anweisung<\/li>\n<\/ul>\n<p>Die <b>DELETE<\/b>-Anweisung soll in unserem Fall wie folgt lauten:<\/p>\n<pre>DELETE FROM tblKategorien WHERE KategorieID = 12<\/pre>\n<p>Die Abfrage k&ouml;nnen Sie dann per VBA mit einer einzigen Anweisung ausf&uuml;hren:<\/p>\n<pre>CurrentDb.Execute \"qryPTDeleteKategorie\"<\/pre>\n<p>Sie k&ouml;nnen auch die Variante mit dem <b>QueryDefs<\/b>-Objekt verwenden:<\/p>\n<pre>CurrentDb.QueryDefs(\"qryPTDeleteKategorie\").Execute<\/pre>\n<p>Damit haben Sie allerdings noch nicht viel gewonnen: Die Anweisung l&ouml;scht ja nur genau den Datensatz, dessen ID Sie als Kriterium angegeben haben. Immerhin haben wir aber bereits eine Abfrage erstellt, die den richtigen Typ aufweist, die Ver-bin-dungszeichenfolge enth&auml;lt und deren Eigenschaft <b>Liefert Datens&auml;tze <\/b>auf <b>Nein <\/b>eingestellt ist. Diese nutzen wir nun, um gezielt einen bestimmten Datensatz zu l&ouml;schen. Die folgende Prozedur (wie auch die weiteren Beispiele im Modul <b>mdlRDBMSZugriff_DatenBearbeiten<\/b>) erwartet den Prim&auml;rschl&uuml;sselwert des zu l&ouml;schenden Datensatzes als Parameter:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>KategorieLoeschen_PT(lngKategorieID<span style=\"color:blue;\"> As Long<\/span>)\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Dim <\/span>qdf<span style=\"color:blue;\"> As <\/span>DAO.QueryDef\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> qdf = db.QueryDefs(\"qryPTDeleteKategorie\")\r\n     qdf.SQL = \"DELETE FROM dbo.tblKategorien  WHERE KategorieID = \" & lngKategorieID\r\n     qdf.Execute\r\n     <span style=\"color:blue;\">Set<\/span> qdf = Nothing\r\n     <span style=\"color:blue;\">Set<\/span> db = Nothing\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Mit dieser Prozedur referenzieren wir die soeben erstellte Abfrage <b>qryPTDeleteKategorie <\/b>und &auml;ndern die enthaltene SQL-Anweisung so, dass diese als Kriterium den per Parameter &uuml;bergebenen Prim&auml;rschl&uuml;sselwert enth&auml;lt.<\/p>\n<p>Danach f&uuml;hren wir die ge&auml;nderte Abfrage mit der <b>Execute<\/b>-Anweisung aus. Der Aufruf dieser Prozedur sieht etwa so aus:<\/p>\n<pre>KategorieLoeschen_PT 104<\/pre>\n<p>Diese Variante hat noch folgende Nachteile:<\/p>\n<ul>\n<li>Die an den SQL Server &uuml;bergebene SQL-Anweisung wird dynamisch zusammengesetzt. Wenn sich die SQL-Anweisung dabei von einer bereits verwendeten unterscheidet, also etwa ein anderer Parameterwert zum Einsatz kommt, muss der Ausf&uuml;hrungsplan neu erstellt werden.<\/li>\n<li>Die Verbindungszeichenfolge ist in der Abfrage gespeichert. Wenn sich diese &auml;ndert, muss sie in jeder Abfrage angepasst werden.<\/li>\n<li>Wir erfahren nicht, ob die Aktion erfolgreich war und wie viele Datens&auml;tze gel&ouml;scht wurden.<\/li>\n<\/ul>\n<p>In den folgenden beiden Abschnitten k&uuml;mmern wir uns um diese Nachteile.<\/p>\n<h2>Datensatz l&ouml;schen per gespeicherter Prozedur<\/h2>\n<p>Als Erstes sorgen wir daf&uuml;r, dass der SQL Server unabh&auml;ngig vom &uuml;bergebenen Parameter nur einen Ausf&uuml;hrungsplan f&uuml;r die Abfrage erstellt, speichert und bei weiteren Aufrufen wiederverwendet. Dazu erstellen wir eine gespeicherte Prozedur, und zwar mit folgendem SQL-Skript:<\/p>\n<pre>CREATE PROCEDURE dbo.spDELETEKategorieNachID (@KategorieID int)\r\nAS\r\nSET NOCOUNT ON;\r\nDELETE FROM tblKategorien \r\nWHERE KategorieID = @KategorieID;<\/pre>\n<p>Dieses Skript k&ouml;nnen Sie, wenn Sie es von Access aus ausf&uuml;hren m&ouml;chten, in das Formular <b>frmSQLBefehle <\/b>eingeben und dann mit der <b>Ausf&uuml;hren<\/b>-Schaltfl&auml;che ausf&uuml;hren (s. Bild 2). Ob die gespeicherte Prozedur erfolgreich angelegt wurde, k&ouml;nnen Sie mit der folgenden Anweisung, ebenfalls in diesem Formular abgesetzt, pr&uuml;fen:<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_01\/pic_1074_002.png\" alt=\"Anlegen einer gespeicherten Prozedur per Access-Formular\" width=\"549,6265\" height=\"438,1459\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Anlegen einer gespeicherten Prozedur per Access-Formular<\/span><\/b><\/p>\n<pre>SELECT * FROM Suedsturm.information_schema.routines \r\nWHERE routine_type = ''PROCEDURE''<\/pre>\n<p>Die gespeicherte Prozedur <b>spDELETEKategorieNachID <\/b>erwartet den Prim&auml;rschl&uuml;sselwert des zu l&ouml;schenden Datensatzes als Parameter. Wenn Sie die gespeicherte Prozedur direkt vom Abfragefenster des SQL Servers aus ausf&uuml;hren wollten, w&uuml;rden Sie dies mit folgender Anweisung erledigen:<\/p>\n<pre>EXEC dbo.spDELETEKategorieNachID 105<\/pre>\n<p>Sie k&ouml;nnen auch diese Abfrage im Formular <b>frmSQLBefehle <\/b>absetzen, aber es gibt noch eine andere Variante &#8211; zum Beispiel f&uuml;r den Fall, dass Sie diese gespeicherte Prozedur per Code aufrufen wollen.<\/p>\n<p>Also erstellen Sie zun&auml;chst eine neue Abfrage, wandeln diese in eine Pass-Through-Abfrage um und legen den SQL-Ausdruck aus Bild 3 fest.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_01\/pic_1074_003.png\" alt=\"Aufruf einer gespeicherten Abfrage per Passthrough-Abfrage\" width=\"424,7115\" height=\"315,4531\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 3: Aufruf einer gespeicherten Abfrage per Passthrough-Abfrage<\/span><\/b><\/p>\n<p>In dieser Abfrage m&uuml;ssen Sie nun nat&uuml;rlich ebenfalls den Prim&auml;rschl&uuml;sselwert des zu l&ouml;schenden Datensatzes als Parameter angeben. Dies erledigen Sie &auml;hnlich wie oben:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>KategorieLoeschen_PT_SP(lngKategorieID<span style=\"color:blue;\"> As Long<\/span>)\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Dim <\/span>qdf<span style=\"color:blue;\"> As <\/span>DAO.QueryDef\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> qdf = db.QueryDefs(\"qryPTDeleteKategorie\")\r\n     qdf.SQL = \"EXEC dbo.spDELETEKategorieNachID \"  & lngKategorieID\r\n     qdf.Execute\r\n     <span style=\"color:blue;\">Set<\/span> qdf = Nothing\r\n     <span style=\"color:blue;\">Set<\/span> db = Nothing\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Der Aufruf sieht beispielsweise wie folgt aus:<\/p>\n<pre>KategorieLoeschen_PT_SP 106<\/pre>\n<p>Dies &auml;ndert zun&auml;chst den SQL-Ausdruck der Abfrage <b>ptKategorieLoeschen <\/b>wie folgt:<\/p>\n<pre>EXEC dbo.spDELETEKategorieNachID 106<\/pre>\n<p>Dieser Aufruf wird direkt an den SQL Server gesendet, der dann die gespeicherte Prozedur <b>spDELETEKategorie-NachID <\/b>mit dem angegebenen Parameter ausf&uuml;hrt und den entsprechenden Datensatz l&ouml;scht.<\/p>\n<h2>Pass-Through-Abfrage mit dynamischer Verbindungszeichenfolge<\/h2>\n<p>Nun soll noch die Verbindungszeichenfolge direkt aus der Tabelle <b>tblVerbindungszeichenfolgen <\/b>bezogen werden (Erl&auml;uterungen zu dieser Tabelle siehe <b>RDBMS-Zugriff per VBA: Verbindungen<\/b>, <b>www.access-im-unternehmen.de\/1054<\/b>). Dazu &uuml;bergeben Sie der VBA-Prozedur noch die ID der Verbindungszeichenfolge als weiteren Parameter. Dieser Parameter wird an die in dem oben erw&auml;hnten Beitrag erl&auml;uterte Funktion <b>Ver-bin-dungs-zeichenfolgeNachID <\/b>&uuml;bergeben, die dann die Verbindungszeichenfolge zur&uuml;ckliefert. Das Ergebnis landet direkt in der Eigenschaft <b>Connect <\/b>des <b>QueryDef<\/b>-Objekts, was dem Zuweisen der Verbindungszeichenfolge zur Eigenschaft <b>ODBC-Verbindung <\/b>entspricht. Die Prozedur finden Sie in Listing 1. Auch hier noch ein Beispielaufruf:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>KategorieLoeschenNachID_PT_SP_Connection(lngKategorieID<span style=\"color:blue;\"> As Long<\/span>, _\r\n         lngVerbindungszeichenfolgeID<span style=\"color:blue;\"> As Long<\/span>)\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Dim <\/span>qdf<span style=\"color:blue;\"> As <\/span>DAO.QueryDef\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> qdf = db.QueryDefs(\"qryPTDELETEKategorie\")\r\n     qdf.Connect = VerbindungszeichenfolgeNachID(lngVerbindungszeichenfolgeID)\r\n     qdf.SQL = \"EXEC dbo.spDELETEKategorieNachID \" & lngKategorieID\r\n     qdf.Execute\r\n     <span style=\"color:blue;\">Set<\/span> qdf = Nothing\r\n     <span style=\"color:blue;\">Set<\/span> db = Nothing\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Aufruf einer gespeicherten Prozedur mit dynamischer Verbindungszeichenfolge<\/span><\/b><\/p>\n<pre>KategorieLoeschenNachID_PT_SP_Connection 107, 9<\/pre>\n<p>Dies l&ouml;scht den Datensatz mit dem Wert <b>107 <\/b>im Feld <b>KategorieID <\/b>und verwendet die Verbindungszeichenfolge mit dem Wert <b>9 <\/b>im Feld <b>VerbindungszeichenfolgeID <\/b>der Tabelle <b>tblVerbindungszeichenfolgen<\/b>.<\/p>\n<p>Sie k&ouml;nnen die Verbindungszeichenfolge nat&uuml;rlich auch mit der Funktion <b>Standard-ver-bin-dungs-zeichenfolge <\/b>ermitteln. Dazu ersetzen Sie die Zeile mit der <b>Connect<\/b>-Eigenschaft wie folgt:<\/p>\n<pre>qdf.Connect = Standardverbindungszeichenfolge<\/pre>\n<p>Oder Sie &uuml;bergeben die Standardverbindungszeichenfolge beim Aufruf:<\/p>\n<pre>KategorieLoeschenNachID_PT_SP_Connection 108,  StandardverbindungszeichenfolgeID<\/pre>\n<h2>L&ouml;schen mit Best&auml;tigung<\/h2>\n<p>Schlie&szlig;lich m&ouml;chten Sie vielleicht noch wissen, ob der L&ouml;schvorgang &uuml;berhaupt erfolgreich war beziehungsweise wie viele Datens&auml;tze von der Aktionsabfrage betroffen waren. T-SQL bietet mit der Funktion <b>@@ROWCOUNT <\/b>ein Mittel, um die Anzahl der von der zuletzt ausgef&uuml;hrten Abfrage betroffenen Datens&auml;tze zu ermitteln. Dies bezieht sich auf die Aktionsabfragen der aktuellen Verbindung. Die folgende gespeicherte Prozedur l&ouml;scht wie in den obigen Beispielen einen Datensatz mit dem &uuml;bergebenen Wert f&uuml;r das Feld <b>KategorieID<\/b>, gibt aber als Ergebnis die Anzahl der betroffenen Datens&auml;tze zur&uuml;ck:<\/p>\n<pre>CREATE PROCEDURE dbo.spDELETEKategorieNachIDMitErgebnis\r\n@KategorieID INT\r\nAS\r\nSET NOCOUNT ON;\r\nDELETE FROM tblKategorien WHERE KategorieID = @KategorieID\r\nSELECT @@ROWCOUNT AS RecordsAffected;<\/pre>\n<p>Wenn Sie diese gespeicherte Prozedur im Abfragefenster im <b>SQL Server Management Studio <\/b>aufrufen, sieht das Ergebnis wie in Bild 4 aus. Um dieses Ergebnis von Access aus zu nutzen, ist eine kleine &auml;nderung am Entwurf der Pass-Through-Abfrage n&ouml;tig.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_01\/pic_1074_004.png\" alt=\"Ergebnis einer gespeicherten Prozedur im SQL Server Management Studio\" width=\"424,7115\" height=\"274,7071\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 4: Ergebnis einer gespeicherten Prozedur im SQL Server Management Studio<\/span><\/b><\/p>\n<p>Wir haben die Abfrage von oben unter dem Namen <b>qrySPDELETEKategorieNachIDMitErgebnis<\/b> kopiert und die Eigenschaft <b>Liefert Datens&auml;tze <\/b>auf den Wert <b>Ja <\/b>eingestellt (s. Bild 5). Anderenfalls liefert die Abfrage das Ergebnis der <b>SELECT<\/b>-Abfrage mit der Anzahl der betroffenen Datens&auml;tze nicht zur&uuml;ck!<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_01\/pic_1074_005.png\" alt=\"Entwurf der Passthrough-Abfrage zum L&ouml;schen eines Datensatzes mit R&uuml;ckgabe der betroffenen Datens&auml;tze\" width=\"549,6265\" height=\"370,1156\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 5: Entwurf der Passthrough-Abfrage zum L&ouml;schen eines Datensatzes mit R&uuml;ckgabe der betroffenen Datens&auml;tze<\/span><\/b><\/p>\n<p>F&uuml;hren Sie diese Abfrage direkt aus, liefert sie das Ergebnis aus Bild 6.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_01\/pic_1074_006.png\" alt=\"Ergebnis der gespeicherten Prozedur innerhalb einer Pass-Through-Abfrage in Access\" width=\"424,7115\" height=\"142,5818\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 6: Ergebnis der gespeicherten Prozedur innerhalb einer Pass-Through-Abfrage in Access<\/span><\/b><\/p>\n<p>Dies ist ein Ergebnis, mit dem wir auch unter VBA arbeiten k&ouml;nnen. Die Prozedur aus Listing 2 verwendet wieder die <b>KategorieID<\/b> und ermittelt die Verbindungszeichenfolge mit der Funktion <b>Stan-dard-verbindungszeichenfolge<\/b>. Sie erzeugt wie gewohnt ein <b>QueryDef<\/b>-Objekt auf Basis einer neuen gespeicherten Access-Abfrage namens <b>spDELETEKategorie-NachIDMitErgebnis <\/b>und ermittelt die gew&uuml;nschte Ver-bin-dungs-zei-chenfolge. Dann weist sie wie zuvor den neuen SQL-Ausdruck zu, f&uuml;hrt die Abfrage aber diesmal nicht mit <b>Execute <\/b>aus. Stattdessen erstellt sie ein neues <b>Recordset<\/b>-Objekt und f&uuml;llt es &uuml;ber die <b>OpenRecordset<\/b>-Methode mit dem Ergebnis der gespeicherten Prozedur. Dies erzeugt ein herk&ouml;mmliches <b>Recordset<\/b>-Objekt, das nur einen Datensatz mit einem Feld enth&auml;lt &#8211; und dieses wird mit <b>rst!RecordsetAffected<\/b> ausgelesen und in einem Meldungsfenster ausgegeben.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>KategorieLoeschenNachID_PT_SP_Connection_MitErgebnis(lngKategorieID<span style=\"color:blue;\"> As Long<\/span>)\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Dim <\/span>qdf<span style=\"color:blue;\"> As <\/span>DAO.QueryDef\r\n     <span style=\"color:blue;\">Dim <\/span>rst<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Dim <\/span>lngAnzahl<span style=\"color:blue;\"> As Long<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> qdf = db.QueryDefs(\"qryPTSPDELETEKategorieNachIDMitErgebnis\")\r\n     qdf.Connect = Standardverbindungszeichenfolge\r\n     qdf.SQL = \"EXEC dbo.spDELETEKategorieNachIDMitErgebnis \" & lngKategorieID\r\n     <span style=\"color:blue;\">Set<\/span> rst = qdf.OpenRecordset(dbOpenSnapshot)\r\n     lngAnzahl = rst!RecordsAffected\r\n     <span style=\"color:blue;\">MsgBox<\/span> \"Es wurden \" & lngAnzahl & \" Datens&auml;tze gel&ouml;scht.\"\r\n     <span style=\"color:blue;\">Set<\/span> rst = Nothing\r\n     <span style=\"color:blue;\">Set<\/span> qdf = Nothing\r\n     <span style=\"color:blue;\">Set<\/span> db = Nothing\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><!--30percent--><\/p>\n<p><b><span style=\"color:darkgrey;\">Listing 2: Aufruf einer gespeicherten Prozedur mit R&uuml;ckgabewert<\/span><\/b><\/p>\n<h2>Dynamische Aktionsabfrage ohne R&uuml;ckgabewert<\/h2>\n<p>Die bisherigen Ans&auml;tze gingen davon aus, dass die Access-Datenbank eine gespeicherte Access-Abfrage mit den wichtigsten Eigenschaften zum Ausf&uuml;hren der gespeicherten Prozedur per Pass-Through-Abfrage enth&auml;lt. Je mehr solcher Abfragen Sie verwenden, desto un&uuml;bersichtlicher wird es im Navigationsbereich. Und davon abgesehen &auml;ndern wir ohnehin zumindest den SQL-Code jeder Pass-Through-Abfrage, die eine gespeicherte Prozedur mit Parametern ausf&uuml;hrt. Dann k&ouml;nnten wir diese auch gleich neu anlegen &#8211; der Performance-Unterschied d&uuml;rfte sich in Grenzen halten. Es gibt jedoch auch die M&ouml;glichkeit, ein <b>QueryDef<\/b>-Objekt komplett tempor&auml;r zu erzeugen.<\/p>\n<p>Was ben&ouml;tigen wir also im Vergleich zur vorherigen Variante Eigentlich m&uuml;ssen wir nur den Namen der zu verwendenden gespeicherten Prozedur zus&auml;tzlich &uuml;bergeben, die Ver-bin-dungs-zeichenfolge und die Parameter werden ja bereits verarbeitet. Au&szlig;erdem referenzieren wir nicht &uuml;ber die <b>QueryDefs<\/b>-Auflistung eine bestehende Abfrage, sondern erstellen mit <b>CreateQueryDef<\/b> eine neue &#8211; und zwar mit einer leeren Zeichenkette als Name. Die Prozedur sieht nun wie in Listing 3 aus. Sie erstellt mit der <b>CreateQueryDef<\/b>-Methode eine tempor&auml;re Abfrage, was wir dadurch erreichen, dass wir eine leere Zeichenfolge als Parameter &uuml;bergeben. <\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>TemporaerePTSPMitParameterAusfuehren(strStoredProcedure<span style=\"color:blue;\"> As String<\/span>, _\r\n         lngVerbindungszeichenfolgeID<span style=\"color:blue;\"> As Long<\/span>, _\r\n         ParamArray varParameter()<span style=\"color:blue;\"> As Variant<\/span>)\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Dim <\/span>qdf<span style=\"color:blue;\"> As <\/span>DAO.QueryDef\r\n     <span style=\"color:blue;\">Dim <\/span>strParameter<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>var<span style=\"color:blue;\"> As Variant<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> qdf = db.CreateQueryDef(\"\")\r\n     For Each var In varParameter\r\n         strParameter = strParameter & \", \" & var\r\n     <span style=\"color:blue;\">Next<\/span> var\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(strParameter) &gt; 0<span style=\"color:blue;\"> Then<\/span>\r\n         strParameter = <span style=\"color:blue;\">Mid<\/span>(strParameter, 3)\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">With<\/span> qdf\r\n         .Connect = VerbindungszeichenfolgeNachID(lngVerbindungszeichenfolgeID)\r\n         .ReturnsRecords = <span style=\"color:blue;\">False<\/span>\r\n         .SQL = \"EXEC \" & strStoredProcedure & \" \" & strParameter\r\n         .Execute\r\n     End <span style=\"color:blue;\">With<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = Nothing\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 3: Prozedur, welche die angegebene gespeicherte Prozedur mit gegebener Verbindungszeichenfolge und Parametern ausf&uuml;hrt<\/span><\/b><\/p>\n<p>F&uuml;r die Parameterliste verwenden wir im Kopf der Prozedur einen Parameter namens <b>varParameter <\/b>des Typs <b>ParamArray<\/b>, dem man beliebig viele durch Kommata getrennte Parameterwerte &uuml;bergeben kann. Die damit &uuml;bergebenen Werte setzt die Prozedur in einer <b>For Each<\/b>-Schleife &uuml;ber alle Elemente von <b>varParameter <\/b>zusammen und stellt jeweils ein Komma voran. Das erste Komma wird danach gegebenenfalls abgeschnitten. Die folgenden Zeilen stellen die Ver-bin-dungszeichenfolge ein, legen f&uuml;r <b>ReturnRecords<\/b> den Wert <b>False <\/b>fest und f&uuml;gen das Schl&uuml;ssel-wort <b>EXEC<\/b>, den Namen der gespeicherten Prozedur und die Parameterliste zusammen. Die <b>Execute<\/b>-Methode f&uuml;hrt die Abfrage schlie&szlig;lich durch. Ein Beispielaufruf sieht etwa so aus:<\/p>\n<pre>TemporaerePTSPMitParameterAusfuehren  \"spDELETEKategorieNachIDMitErgebnis\",9,112<\/pre>\n<p>Um auch hier die Standardverbindungszeichenfolge zu verwenden, nutzen Sie folgenden Aufruf:<\/p>\n<pre>TemporaerePTSPMitParameterAusfuehren  \"spDELETEKategorieNachIDMitErgebnis\", _\r\n StandardverbindungszeichenfolgeID, 112<\/pre>\n<h2>Autowert des zuletzt hinzugef&uuml;gten Datensatzes ermitteln<\/h2>\n<p>F&uuml;r verschiedene Zwecke ist es interessant, den Autowert des zuletzt hinzugef&uuml;gten Datensatzes zu ermitteln. Dies gilt nur f&uuml;r die VBA-Varianten: also das Hinzuf&uuml;gen mit der DAO-Methode <b>AddNew <\/b>oder mit der per <b>Execute <\/b>aufgerufenen <b>INSERT INTO<\/b>&#8211; oder <b>SELECT INTO<\/b>-SQL-Anweisung. Die folgenden Abschnitte zeigen die Variante f&uuml;r lokale Tabellen sowie f&uuml;r das Hinzuf&uuml;gen von Datens&auml;tzen zu Tabellen einer SQL Server-Datenbank.<\/p>\n<h2>Variante I: AddNew\/Update<\/h2>\n<p>Die erste Variante ist die oft verwendete DAO-Methode mit den beiden Anweisungen <b>AddNew <\/b>und <b>Update<\/b>, wobei zwischen diesen beiden Anweisungen die neuen Feldwerte angegeben werden. Dies ist die Access-Variante, bei welcher der Wert des Prim&auml;rschl&uuml;sselfelds f&uuml;r den neuen Datensatz bereits nach dem Aufruf der <b>AddNew<\/b>-Methode belegt ist und ausgelesen werden kann &#8211; hier am Beispiel der lokalen Tabelle <b>tblKategorien_Lokal<\/b>:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>NeuerDatensatzMitID_Lokal()\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Dim <\/span>rst<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> rst = db.OpenRecordset( \"SELECT * FROM tblKategorien_Lokal\",  dbOpenDynaset)\r\n     rst.Add<span style=\"color:blue;\">New<\/span>\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"Neue KategorieID: \" & rst!KategorieID\r\n     rst!Kategoriename = \"Beispielkategorie\"\r\n     rst!Beschreibung = \"Beispielbeschreibung\"\r\n     rst.Update\r\n     rst.Close\r\n     <span style=\"color:blue;\">Set<\/span> rst = Nothing\r\n     <span style=\"color:blue;\">Set<\/span> db = Nothing\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>F&uuml;hren wir diese Prozedur in einer Beispieldatenbank aus, die eine per ODBC verkn&uuml;pfte SQL Ser-ver-Ta-belle namens <b>tblKategorien <\/b>enth&auml;lt, liefert dies den Fehler aus Bild 7. Dies besagt, dass Sie auf eine SQL Server-Tabelle mit einem Prim&auml;rschl&uuml;sselwert (<b>IDENTITY<\/b>) nur zu-grei-fen k&ouml;nnen, wenn Sie den Parameter <b>dbSeeChanges <\/b>verwenden. Diesen Fehler beheben wir in der folgenden Variante. Dort f&uuml;gen wir in der Zeile mit der <b>Open-Re-cordset<\/b>-Methode zun&auml;chst den Wert <b>dbSeeChanges <\/b>f&uuml;r den dritten Parameter hinzu:<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_01\/pic_1074_007.png\" alt=\"Fehler beim Versuch, ein Recordset ohne die Option db-See-Changes zu &ouml;ffnen\" width=\"424,7115\" height=\"241,078\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 7: Fehler beim Versuch, ein Recordset ohne die Option db-See-Changes zu &ouml;ffnen<\/span><\/b><\/p>\n<pre><span style=\"color:blue;\">Set<\/span> rst = db.OpenRecordset(\"SELECT * FROM tblKatgorien\", dbOpenDynaset, dbSeeChanges)<\/pre>\n<p>Die Prozedur l&auml;uft nun durch, die <b>Debug.Print<\/b>-Anweisung liefert jedoch keinen Wert f&uuml;r das Feld <b>KategorieID<\/b>, also das durch den SQL Server mit einem Autowert zu f&uuml;llende Pri-m&auml;r-schl&uuml;sselfeld:<\/p>\n<pre><span style=\"color:blue;\">Debug.Print<\/span> \"Neue KategorieID: \" & rst!KategorieID\r\nNeue KategorieID:<\/pre>\n<p>Der Grund liegt darin, dass der SQL Server den Autowert f&uuml;r das Prim&auml;rschl&uuml;sselfeld erst vergibt, wenn der Datensatz gespeichert wird. Hilft es also, wenn wir den Wert des Prim&auml;rschl&uuml;sselfeldes erst nach dem Aufruf von <b>Update <\/b>abfragen Nein &#8211; <b>AddNew<\/b> und <b>Update <\/b>verschieben den Da-ten-satzzeiger nicht auf den neuen Datensatz, sondern dieser verbleibt auf dem Datensatz, auf den der Zeiger vor dem Anlegen bereits zeigte (in diesem Fall auf den ersten Datensatz des Recordsets):<\/p>\n<pre>rst.Add<span style=\"color:blue;\">New<\/span>\r\nrst!Kategoriename = \"Beispielkategorie1\"\r\nrst!Beschreibung = \"Beispielbeschreibung\"\r\nrst.Update\r\n<span style=\"color:blue;\">Debug.Print<\/span> \"Neue KategorieID: \" & rst!KategorieID<\/pre>\n<p>Hier kommt die Eigenschaft <b>LastModified <\/b>des DAO-Recordsets zum Einsatz. Stellen wir die Ei-gen-schaft <b>Bookmark <\/b>nach dem Einf&uuml;gen des Datensatzes auf den Wert der Eigenschaft <b>Last-Mo-di-fied <\/b>ein, wird der Datensatzzeiger auf den neuen Datensatz verschoben und wir k&ouml;nnen den Prim&auml;rschl&uuml;sselwert auslesen:<\/p>\n<pre>rst.Update\r\nrst.Bookmark = rst.LastModified\r\n<span style=\"color:blue;\">Debug.Print<\/span> \"Neue KategorieID: \" & rst!KategorieID<\/pre>\n<h2>Fehler ermitteln unter DAO<\/h2>\n<p>Wenn Sie das obige Beispiel zweimal hintereinander ausf&uuml;hren, ohne den Code zu &auml;ndern, l&ouml;st dies den Fehler aus Bild 8 aus. Die Fehlermeldung <b>ODBC-Aufruf fehlgeschlagen <\/b>weist lediglich darauf hin, dass beim Zugriff auf den SQL Server ein Fehler aufgetreten ist. Genauere Informationen erhalten Sie &uuml;ber die <b>Errors<\/b>-Auflistung der DAO-Bibliothek. Die <b>Count<\/b>-Eigenschaft liefert die Anzahl der Eintr&auml;ge &#8211; im Direktbereich etwa so (nach dem Quittieren der obigen Fehlermeldung mit einem Klick auf die Schaltfl&auml;che <b>Debuggen<\/b>):<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_01\/pic_1074_008.png\" alt=\"Fehler beim Versuch, einen bereits vorhandenen Wert in ein eindeutiges Feld zu schreiben\" width=\"424,7115\" height=\"241,078\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 8: Fehler beim Versuch, einen bereits vorhandenen Wert in ein eindeutiges Feld zu schreiben<\/span><\/b><\/p>\n<pre>  DAO.Errors.Count\r\n  3 <\/pre>\n<p>Der letzte Fehler der Auflistung mit dem Index <b>2 <\/b>ist der, den auch das <b>Err<\/b>-Objekt und die Fehlermeldung liefern:<\/p>\n<pre>  DAO.Errors(2).Number, DAO.Errors(2).Description\r\n  3146         ODBC-Aufruf fehlgeschlagen.<\/pre>\n<p>Der zweite Fehler basiert auf folgender Nummer und Meldung:<\/p>\n<pre>  DAO.Errors(1).Number, DAO.Errors(1).Description\r\n  3621         [Microsoft][SQL Server Native Client 11.0][SQL Server]Die Anweisung wurde beendet.<\/pre>\n<p>Auch diese Meldung liefert keine entscheidenden Informationen. Bleibt noch die Meldung mit dem Index <b>0<\/b>:<\/p>\n<pre>  DAO.Errors(0).Number, DAO.Errors(0).Description\r\n  2601         [Microsoft][SQL Server Native Client 11.0][SQL Server]Eine Zeile mit doppeltem _\r\nSchl&uuml;ssel kann in das dbo.tblKategorien-Objekt mit dem eindeutigen IX_tblKategorien-Index _\r\nnicht eingef&uuml;gt werden. Der doppelte Schl&uuml;sselwert ist (Beispielkategorie1).<\/pre>\n<p>Wir haben also schlicht versucht, einen Wert in das Feld <b>Kategoriename <\/b>einzutragen, der bereits vorhanden war. Und da f&uuml;r dieses Feld ein eindeutiger Index festgelegt wurde, l&ouml;st der SQL Server einen entsprechenden Fehler aus.<\/p>\n<h2>Variante II: Execute\/INSERT INTO<\/h2>\n<p>Unter Access verwendet man bei Datensatz&auml;nderungen, die durch <b>INSERT INTO<\/b>&#8211; und <b>SELECT INTO<\/b>-Abfragen durchgef&uuml;hrt wurden, eine spezielle Abfrage zum Ermitteln des Prim&auml;rschl&uuml;sselwertes des zuletzt hinzugef&uuml;gten Datensatzes. Ein Beispiel sieht wie folgt aus:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>NeuerDatensatzExecute()\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Dim <\/span>lngID<span style=\"color:blue;\"> As Long<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     db.Execute \"INSERT INTO tblKategorien(Kategoriename,  Beschreibung) VALUES(''Beispielkategorie'', &quot; _\r\n         &amp; &quot;''Beispielbeschreibung'')\", dbFailOnError\r\n     lngID = db.OpenRecordset(\"SELECT @@IDENTITY\").Fields(0)\r\n     <span style=\"color:blue;\">Debug.Print<\/span> \"Neuer Datensatz: \" & lngID\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Die Codezeilen legen zun&auml;chst einen neuen Datensatz in der Tabelle <b>tblKategorien <\/b>an. Im Gegensatz zur entsprechenden Vorgehensweise mit den <b>DAO<\/b>-Methoden <b>AddNew <\/b>und <b>Up-date <\/b>erhalten Sie hier nicht automatisch die <b>ID <\/b>des neu angelegten Datensatzes. Diese ist aber h&auml;ufig erforderlich, da gegebenenfalls weitere Datens&auml;tze angelegt werden sollen, die per Fremd-schl&uuml;sselwert auf diesen Datensatz verweisen, oder der neu angelegte Datensatz soll gleich nach dem Aktualisieren der Datenherkunft im Formular angezeigt werden. Access liefert die <b>ID <\/b>mit der Funktion <b>@@IDENTITY<\/b>, die Sie in Form einer <b>SELECT<\/b>-Abfrage abfragen.<\/p>\n<h2>Neue ID per SQL Server abfragen<\/h2>\n<p>Der SQL Server bietet gl&uuml;cklicherweise genau die gleiche Funktion an, um den zuletzt hinzugef&uuml;gten Autowert zu ermitteln. Allein die &uuml;bergabe an Access stellt eine kleine H&uuml;rde dar, die wir allerdings leicht nehmen &#8211; zun&auml;chst mit der folgenden gespeicherten Prozedur als Beispiel:<\/p>\n<pre>CREATE PROCEDURE dbo.spINSERTINTOKategorie\r\n@Kategoriename NVARCHAR(255), @Beschreibung NVARCHAR(255)\r\nAS\r\nSET NOCOUNT ON;\r\nINSERT INTO tblKategorien(Kategoriename, Beschreibung) VALUES(@Kategoriename, @Beschreibung);<\/pre>\n<p>Wenn wir nun die gespeicherte Prozedur aufrufen und anschlie&szlig;end die bereits unter Access verwendete Abfrage zur Abfrage des neuen Identit&auml;tswertes, sieht das wie folgt aus:<\/p>\n<pre>EXEC dbo.spINSERTINTOKategorie ''Neue Kategorie'', ''Neue Beschreibung''\r\nSELECT @@IDENTITY AS KategorieID;<\/pre>\n<p>Das Abfragefenster zeigt nun den Wert des Feldes <b>KategorieID<\/b> des neuen Datensatzes an (s. Bild 9).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_01\/pic_1074_009.png\" alt=\"Anlegen eines neuen Datensatzes plus Ausgabe der neuen ID\" width=\"499,6607\" height=\"227,6714\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 9: Anlegen eines neuen Datensatzes plus Ausgabe der neuen ID<\/span><\/b><\/p>\n<p>Neben <b>@@IDENTITY <\/b>bietet T-SQL noch zwei Al-ter-na-ti-ven. Es gibt insgesamt drei Funktionen, die ein &auml;hnliches Ergebnis lie-fern:<\/p>\n<ul>\n<li><b>@@IDENTITY<\/b>: Liefert den Wert des zuletzt angelegten Autowertes. Meist ist dies der Wert, den wir suchen &#8211; aber es gibt Ausnahmen! Wenn Sie einen Trigger einsetzen, der durch das Anlegen des neuen Datensatzes ausgel&ouml;st wird und der wiederum einen neuen Datensatz in einer Tabelle mit einem Autowert-Feld eintr&auml;gt, liefert <b>@@IDENTITY <\/b>den durch den Trigger erzeugten Autowert zur&uuml;ck. Die Funktion ist auf die aktuelle Session begrenzt und liefert nicht etwa durch andere Benutzer angelegte Autowerte zur&uuml;ck.<\/li>\n<li><b>SCOPE_IDENTITY()<\/b>: Diese Funktion ist ebenfalls auf die aktuelle Session beschr&auml;nkt, aber ber&uuml;cksichtigt nur die Autowerte, die explizit erzeugt wurden &#8211; also durch <b>INSERT INTO <\/b>oder <b>SELECT INTO<\/b>-Anweisungen. Durch Trigger erzeugte Datens&auml;tze werden nicht ber&uuml;cksichtigt.<\/li>\n<li><b>IDENT_CURRENT()<\/b>: Der Vollst&auml;ndigkeit halber stellen wir auch noch diese Funktion vor. Sie liefert den zuletzt zu einer bestimmten Tabelle hinzugef&uuml;gten Autowert. Sie erwartet die Angabe des Tabellennamens als Parameter, zum Beispiel <b>IDENT_CURRENT(&#8220;tblKategorien&#8220;)<\/b>.<\/li>\n<\/ul>\n<p>Verwenden wir also statt <b>@@IDENTITY <\/b>die Funktion <b>SCOPE_IDENTITY()<\/b> &#8211; die Klammern d&uuml;rfen &uuml;brigens nicht weggelassen werden:<\/p>\n<pre>SELECT SCOPE_IDENTITY() AS KategorieID<\/pre>\n<p>Wie Bild 10 zeigt, funktioniert die Funktion nicht wie <b>@@IDENTITY<\/b>. In der Tat beschr&auml;nkt sich der G&uuml;ltigkeitsbereich von <b>SCOPE_IDENTITY <\/b>auf eine gespeicherte Prozedur, eine benutzerdefinierte Funktion oder einen Batch. Kein Problem: Packen wir die R&uuml;ckgabe des Autowertes des neu angelegten Datensatzes also einfach mit in die gespeicherte Prozedur:<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_01\/pic_1074_010.png\" alt=\"SCOPE_IDENTITY liefert nicht auf Anhieb den gew&uuml;nschten Wert.\" width=\"499,6607\" height=\"227,6714\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 10: SCOPE_IDENTITY liefert nicht auf Anhieb den gew&uuml;nschten Wert.<\/span><\/b><\/p>\n<pre>CREATE PROCEDURE dbo.spINSERTINTOKategorienMitID\r\n@Kategoriename NVARCHAR(255), @Beschreibung NVARCHAR(255)\r\nAS\r\nSET NOCOUNT ON;\r\nINSERT INTO tblKategorien(Kategoriename, Beschreibung) VALUES(@Kategoriename, @Beschreibung);\r\nSELECT SCOPE_IDENTITY() AS KategorieID;<\/pre>\n<p>Dies liefert das gew&uuml;nschte Ergebnis &#8211; der Aufruf der gespeicherten Prozedur <b>spINSERTINTOKategorienMitID <\/b>gibt gleich den neuen Autowert zur&uuml;ck (s. Bild 11).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_01\/pic_1074_011.png\" alt=\"Als Teil der gespeicherten Prozedur arbeitet SCOPE_IDENTITY hingegen wie gew&uuml;nscht.\" width=\"499,6607\" height=\"227,6714\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 11: Als Teil der gespeicherten Prozedur arbeitet SCOPE_IDENTITY hingegen wie gew&uuml;nscht.<\/span><\/b><\/p>\n<h2>Autowert per gespeicherter Prozedur von Access aus<\/h2>\n<p>Nun wissen wir schon einmal, wie wir den neuen Autowert &#8211; und somit meist auch den Prim&auml;rschl&uuml;sselwert &#8211; im SQL Server ermitteln. Der Rest ist eine Kombination aus dem Erstellen eines <b>QueryDef<\/b>-Objekts und dessen Ausf&uuml;hrung mit der <b>OpenRecordset<\/b>-Methode (s. Listing 4). Die Prozedur erstellt ein neues tempor&auml;res <b>QueryDef<\/b>-Objekt (also mit einer leeren Zeichenkette als Name). F&uuml;r dieses sind einige Eigenschaften einzustellen:<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>NeuenDatensatzMitID_INSERT_InSP()\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>dao.Database\r\n     <span style=\"color:blue;\">Dim <\/span>qdf<span style=\"color:blue;\"> As <\/span>dao.QueryDef\r\n     <span style=\"color:blue;\">Dim <\/span>rst<span style=\"color:blue;\"> As <\/span>dao.Recordset\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> qdf = db.CreateQueryDef(\"\")\r\n     <span style=\"color:blue;\">With<\/span> qdf\r\n         .Connect = Standardverbindungszeichenfolge\r\n         .ReturnsRecords = <span style=\"color:blue;\">True<\/span>\r\n         .SQL = \"EXEC spINSERTINTOKategorienMitID  ''Neue Kategorie 123'', ''Beschreibung''\"\r\n         <span style=\"color:blue;\">Set<\/span> rst = .OpenRecordset\r\n         <span style=\"color:blue;\">Debug.Print<\/span> rst.Fields(\"KategorieID\")\r\n     End <span style=\"color:blue;\">With<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = Nothing\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 4: Prozedur zum Erstellen eines tempor&auml;ren QueryDef-Objekts<\/span><\/b><\/p>\n<ul>\n<li><b>Connect<\/b>: Wird mit der Funktion <b>Standardver-bin-dungs-zei-chen-folge-<\/b> gef&uuml;llt.<\/li>\n<li><b>ReturnsRecord<\/b>: Obwohl die Abfrage eigentlich eine Aktion ausf&uuml;hren soll, m&ouml;chten wir den Wert des neuen Prim&auml;rschl&uuml;ssels erhalten. Also stellen wir <b>ReturnsRecords <\/b>auf <b>True <\/b>ein.<\/li>\n<li><b>SQL<\/b>: Die Anweisung f&uuml;hrt die gew&uuml;nschte gespeicherte Prozedur mit der <b>EXEC<\/b>-Anweisung aus und gibt den Namen der anzulegenden Kategorie als Parameter an.<\/li>\n<\/ul>\n<p>Anschlie&szlig;end f&uuml;hrt die Prozedur die Abfrage aus, indem sie die <b>OpenRecordset<\/b>-Methode aufruft. Das Ergebnis in Form eines Recordsets wird mit der Objektvariablen <b>rst <\/b>referenziert. Die folgende Anweisung greift auf das einzige zur&uuml;ckgegebene Feld namens <b>KategorieID <\/b>zu und liest somit den Autowert des neuen Datensatzes aus.<\/p>\n<p>Was aber geschieht, wenn wir die Pass-Through-Abfrage einfach nur ausf&uuml;hren m&ouml;chten, ohne den neuen Autowert zu beziehen Fliegt uns diese dann um die Ohren Nein &#8211; es sind nur ein paar kleinere &auml;nderungen n&ouml;tig. Die folgende Variante stellt <b>ReturnRecords <\/b>auf <b>False <\/b>ein, au&szlig;erdem wird kein Recordset erstellt, sondern die VBA-Prozedur einfach nur mit <b>Execute <\/b>ausgef&uuml;hrt:<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>NeuenDatensatzMitID_INSERT_InSP_ OhneRueckgabewert()\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>dao.Database\r\n     <span style=\"color:blue;\">Dim <\/span>qdf<span style=\"color:blue;\"> As <\/span>dao.QueryDef\r\n     <span style=\"color:blue;\">Dim <\/span>rst<span style=\"color:blue;\"> As <\/span>dao.Recordset\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> qdf = db.CreateQueryDef(\"\")\r\n     <span style=\"color:blue;\">With<\/span> qdf\r\n         .Connect = Standardverbindungszeichenfolge\r\n         .ReturnsRecords = <span style=\"color:blue;\">False<\/span>\r\n         .SQL = \"EXEC dbo.spINSERTINTOKategorienMitID  ''Neue Kategorie 124'', ''Beschreibung''\"\r\n         .Execute\r\n     End <span style=\"color:blue;\">With<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = Nothing\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<h2>Autowert-Abfrage per Code an gespeicherte Prozedur anh&auml;ngen<\/h2>\n<p>Nicht alle gespeicherten Prozeduren, die Aktionsabfragen wie beispielsweise eine <b>INSERT INTO<\/b>&#8211; oder <b>SELECT INTO<\/b>-Abfrage ausf&uuml;hren, enthalten gleich eine entsprechende <b>SELECT<\/b>-Anweisung, welche den zuletzt hinzugef&uuml;gten Autowert ermittelt. Es kann beispielsweise sein, dass Sie ein Access-Frontend gegen ein SQL Server-Back-end programmieren m&uuml;ssen, in dem Sie keine &auml;nderungen durchf&uuml;hren k&ouml;nnen oder d&uuml;rfen. Wie h&auml;ngen Sie dennoch eine Anweisung wie <b>SELECT SCOPE_IDENTITY() &#8230; <\/b>so an die gespeicherte Prozedur an, dass diese im gleichen G&uuml;ltig-keits-bereich ausgef&uuml;hrt wird Die Antwort ist: Es gelingt nicht. Die gespeicherte Prozedur ist ein abgeschlossener G&uuml;ltigkeitsbereich, also Scope, dessen &auml;nderungen nach der Ausf&uuml;hrung nicht &uuml;ber Funktionen wie <b>SCOPE_IDENTITY() <\/b>abgefragt werden k&ouml;nnen.<\/p>\n<p>Sollten Sie Daten an eine Tabelle hinzuf&uuml;gen wollen und m&uuml;ssen den Autowert ermitteln, k&ouml;nnen Sie nur direkt die <b>INSERT INTO<\/b>-Anweisung und die <b>SELECT SCOPE_IDENTITY()&#8230;<\/b>-Abfrage gleichzeitig &uuml;ber eine Pass-Through-Abfrage absetzen.<\/p>\n<h2>Allgemeine Prozeduren f&uuml;r das Ausf&uuml;hren von Aktionsabfragen<\/h2>\n<p>Zum Ausf&uuml;hren von Aktionsabfragen haben wir zwei verschiedene Routinen vorgesehen &#8211; eine mit und eine ohne R&uuml;ckgabewert.<\/p>\n<h2>Aktionsabfrage ohne Ergebnis<\/h2>\n<p>Die einfachere ist die ohne R&uuml;ckgabewert. Sie rufen diese Prozedur beispielsweise wie folgt auf (diese Prozedur finden Sie im Modul <b>mdlToolsSQLServer<\/b>, die Beispiele im Modul <b>mdlRDBMSZugriff_DatenBearbeiten<\/b>):<\/p>\n<pre>SPAktionsabfrageOhneErgebnis  \"dbo.spINSERTINTOKategorienMitID\",  Standardverbindungszeichenfolge, _\r\n\"Neue Kategorie 234\",  \"Beschreibung\"<\/pre>\n<p>Dies ruft die gespeicherte Prozedur <b>dbo.spINSERTINTOKategorienMitID <\/b>auf und &uuml;bergibt die folgenden Parameter:<\/p>\n<ul>\n<li><b>strStoredProcedure<\/b>: Name der auszuf&uuml;hrenden gespeicherten Prozedur, hier <b>dbo.spINSERT-INTOKategorienMitID<\/b><\/li>\n<li><b>strVerbindungszeichenfolge<\/b>: zu verwendende Verbindungszeichenfolge, hier mit der Funk-tion <b>Standardverbindungszeichenfolge <\/b>ermittelt<\/li>\n<li><b>varParameter<\/b>: Parameter-Array, das beliebig viele Parameter entgegennehmen kann &#8211; hier nur der Name der neuen Kategorie und der Beschreibung<\/li>\n<\/ul>\n<p>Die Prozedur erstellt ein neues <b>QueryDef<\/b>-Objekt ohne Name, da dieses nur tempor&auml;r innerhalb dieser Prozedur genutzt werden soll. Dann setzt sie mit der Hilfsfunktion <b>Parameterliste<\/b> die eventuell in <b>varParameter <\/b>enthaltenen Werte zusammen &#8211; siehe weiter unten.<\/p>\n<p>Nun stattet die Prozedur die Pass-Through-Abfrage mit den notwendigen Informationen aus:<\/p>\n<ul>\n<li><b>Connect <\/b>erh&auml;lt die Verbindungszeichenfolge,<\/li>\n<li><b>ReturnsRecords <\/b>wird auf <b>False <\/b>eingestellt, da keine Daten zur&uuml;ckgeliefert werden sollen,<\/li>\n<li><b>SQL <\/b>wird mit einem Ausdruck gef&uuml;llt, der sich aus der <b>EXEC<\/b>-Anweisung, dem Namen der gespeicherten Prozedur und der Parameterliste zusammensetzt und<\/li>\n<li>die <b>Execute<\/b>-Methode f&uuml;hrt die Abfrage aus.<\/li>\n<\/ul>\n<p>Die VBA-Prozedur sieht schlie&szlig;lich wie in Listing 5 aus.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>SPAktionsabfrageOhneErgebnis(strStoredProcedure<span style=\"color:blue;\"> As String<\/span>, _\r\n         strVerbindungszeichenfolge<span style=\"color:blue;\"> As String<\/span>, _\r\n         ParamArray varParameter()<span style=\"color:blue;\"> As Variant<\/span>)\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>dao.Database\r\n     <span style=\"color:blue;\">Dim <\/span>qdf<span style=\"color:blue;\"> As <\/span>dao.QueryDef\r\n     <span style=\"color:blue;\">Dim <\/span>strParameter<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> qdf = db.CreateQueryDef(\"\")\r\n     strParameter = Parameterliste(varParameter)\r\n     <span style=\"color:blue;\">With<\/span> qdf\r\n         .Connect = strVerbindungszeichenfolge\r\n         .ReturnsRecords = <span style=\"color:blue;\">False<\/span>\r\n         .SQL = \"EXEC \" & strStoredProcedure & \" \" & strParameter\r\n         .Execute\r\n     End <span style=\"color:blue;\">With<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = Nothing\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 5: Prozedur zum Ausf&uuml;hren beliebiger gespeicherter Prozeduren mit Parametern, aber ohne R&uuml;ckgabewert<\/span><\/b><\/p>\n<h2>Aktionsabfrage mit Ergebnis<\/h2>\n<p>Diese Routine zur Durchf&uuml;hrung einer Aktionsabfrage funktioniert prinzipiell genauso wie die zuvor beschriebene Variante. Allerdings erwartet sie einen einzelnen R&uuml;ckgabewert.<\/p>\n<p>Das Be-son-dere ist, dass der R&uuml;ckgabewert entweder in der gespeicherten Prozedur in Form einer entsprechenden <b>SELECT<\/b>-Abfrage implementiert werden kann (<b>bolRueckgabeInSPImplementiert <\/b>erh&auml;lt den Wert <b>True<\/b>) oder dass die Prozedur einfach die Anzahl der von der Aktionsabfrage betroffenen Datens&auml;tze ermittelt oder eine mit dem Parameter <b>strRueckgabeausdruck <\/b>festgelegte <b>SELECT<\/b>-Anweisung an die <b>EXEC<\/b>-Methode mit der Angabe der gespeicherten Prozedur anh&auml;ngt.<\/p>\n<p>Achtung: Die gespeicherte Prozedur muss die Anweisung <b>SET NOCOUNT OFF <\/b>enthalten, damit <b>@@ROWCOUNT <\/b>die Anzahl der betroffenen Datens&auml;tze erfassen kann.<\/p>\n<p>Der Rest entspricht dem Aufruf der zuvor beschriebenen Prozedur <b>SPAktions-ab-frage-Ohne-Er-geb-nis<\/b> (s. Listing 6).<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>SPAktionsabfrageMitErgebnis(strStoredProcedure<span style=\"color:blue;\"> As String<\/span>, _\r\n         strVerbindungszeichenfolge<span style=\"color:blue;\"> As String<\/span>, _\r\n         bolRueckgabeInSPImplementiert<span style=\"color:blue;\"> As Boolean<\/span>, strRueckgabeausdruck<span style=\"color:blue;\"> As String<\/span>, _\r\n         ParamArray varParameter()<span style=\"color:blue;\"> As Variant<\/span>)<span style=\"color:blue;\"> As Variant<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>dao.Database\r\n     <span style=\"color:blue;\">Dim <\/span>qdf<span style=\"color:blue;\"> As <\/span>dao.QueryDef\r\n     <span style=\"color:blue;\">Dim <\/span>rst<span style=\"color:blue;\"> As <\/span>dao.Recordset\r\n     <span style=\"color:blue;\">Dim <\/span>strParameter<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> qdf = db.CreateQueryDef(\"\")\r\n     strParameter = Parameterliste(varParameter)\r\n     <span style=\"color:blue;\">With<\/span> qdf\r\n         .Connect = strVerbindungszeichenfolge\r\n         .ReturnsRecords = <span style=\"color:blue;\">True<\/span>\r\n         .SQL = \"EXEC \" & strStoredProcedure & \" \" & strParameter\r\n         <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> bolRueckgabeInSPImplementiert<span style=\"color:blue;\"> Then<\/span>\r\n             <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(strRueckgabeausdruck) = 0<span style=\"color:blue;\"> Then<\/span>\r\n                 .SQL = .SQL & <span style=\"color:blue;\">vbCrLf<\/span> & \"SELECT @@ROWCOUNT AS RecordsAffected;\"\r\n             <span style=\"color:blue;\">Else<\/span>\r\n                 .SQL = .SQL & <span style=\"color:blue;\">vbCrLf<\/span> & strRueckgabeausdruck\r\n             <span style=\"color:blue;\">End If<\/span>\r\n         <span style=\"color:blue;\">End If<\/span>\r\n         <span style=\"color:blue;\">Set<\/span> rst = .OpenRecordset\r\n     End <span style=\"color:blue;\">With<\/span>\r\n     SPAktionsabfrageMitErgebnis = Nz(rst.Fields(0))\r\n     <span style=\"color:blue;\">Set<\/span> db = Nothing\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 6: Prozedur zum Ausf&uuml;hren beliebiger gespeicherter Prozeduren mit Parametern mit R&uuml;ckgabewert<\/span><\/b><\/p>\n<p>Ein Beispielaufruf sieht wie folgt aus. Hier hat <b>bolRueckgabeInSPImplementiert <\/b>den Wert <b>True<\/b>, also wird keine <b>SELECT<\/b>-Anweisung zur Ermittlung eines Ergebnisses angeh&auml;ngt:<\/p>\n<pre><span style=\"color:blue;\">Debug.Print<\/span> SPAktionsabfrageMitErgebnis(\"dbo.spINSERTINTOKategorienMitID\", \r\nStandardverbindungszeichenfolge, True, \"\", \"Neue Kategorie 345\", \"Beschreibung\")<\/pre>\n<p>Das Ergebnis entspricht dann dem von der Funktion <b>SCOPE_IDENTITY <\/b>in der gespeicherten Pro-ze-dur ermittelten Autowert des neuen Datensatzes. Angenommen, es handelt sich um eine reine Aktionsabfrage, dann k&ouml;nnen Sie mit folgendem Aufruf eine <b>SELECT<\/b>-Anweisung mit dem Parameter <b>strRueckgabeausdruck <\/b>&uuml;bergeben, die dann an die <b>EXEC<\/b>-Anweisung mit der gespeicherten Prozedur angeh&auml;ngt wird. Hier soll die Kategorie mit dem Prim&auml;rschl&uuml;sselwert <b>1 <\/b>gel&ouml;scht werden:<\/p>\n<pre><span style=\"color:blue;\">Debug.Print<\/span> SPAktionsabfrageMitErgebnis( \"dbo.spDELETEKategorieNachID\",  \r\nStandardverbindungszeichenfolge, False,  \"SELECT @@ROWCOUNT AS Geloescht\", 1)<\/pre>\n<h2>Zusammenfassung und Ausblick<\/h2>\n<p>Dieser Beitrag erl&auml;utert die Grundlagen f&uuml;r das Durchf&uuml;hren von Datensatzbearbeitungen an den Daten einer SQL Server-Datenbank von VBA-Prozeduren aus.<\/p>\n<p>Zu Beginn haben wir grundlegende Herausforderungen und L&ouml;sungen beschrieben und die verwendeten VBA-Routinen immer weiter verfeinert, bis wir am Ende die beiden Prozeduren <b>SPAktionsabfrageOhneErgebnis <\/b>und <b>SPAktionsabfrageMitErgebnis <\/b>herausgearbeitet haben.<\/p>\n<p>Beide haben Parameter, mit denen Sie die auszuf&uuml;hrenden gespeicherten Prozeduren, die Verbindungszeichenfolge, R&uuml;ckgabewerte und Parameter auswerten k&ouml;nnen.<\/p>\n<h3>Downloads zu diesem Beitrag<\/h3>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>RDBMSPerVBA_DatenBearbeiten.accdb<\/p>\n<p>Suedsturm.sql<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/716B0386-0204-4E29-ADA0-7332DE67F0E5\/aiu_1074.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Im Beitrag &#8222;RDBMS-Zugriff per VBA: Verbindungen&#8220; haben wir die Grundlage f&uuml;r den Zugriff auf SQL Server-Datenbanken geschaffen, &#8222;RDBMS-Zugriff per VBA: Daten abfragen&#8220; zeigt, wie Sie die Daten einer SQL Server-Datenbank ermitteln. Im vorliegenden Teil dieser Beitragsreihe erfahren Sie, wie Sie die Daten einer SQL Server-Datenbank bearbeiten.<\/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":[66012017,662017,44000022],"tags":[],"class_list":["post-55001074","post","type-post","status-publish","format-standard","hentry","category-66012017","category-662017","category-SQL_Server_und_Co"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v20.9 (Yoast SEO v27.3) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>RDBMS-Zugriff per VBA: Daten bearbeiten - Access im Unternehmen<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_bearbeiten\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"RDBMS-Zugriff per VBA: Daten bearbeiten\" \/>\n<meta property=\"og:description\" content=\"Im Beitrag &quot;RDBMS-Zugriff per VBA: Verbindungen&quot; haben wir die Grundlage f&uuml;r den Zugriff auf SQL Server-Datenbanken geschaffen, &quot;RDBMS-Zugriff per VBA: Daten abfragen&quot; zeigt, wie Sie die Daten einer SQL Server-Datenbank ermitteln. Im vorliegenden Teil dieser Beitragsreihe erfahren Sie, wie Sie die Daten einer SQL Server-Datenbank bearbeiten.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_bearbeiten\/\" \/>\n<meta property=\"og:site_name\" content=\"Access im Unternehmen\" \/>\n<meta property=\"article:published_time\" content=\"2020-05-14T13:41:20+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/vg09.met.vgwort.de\/na\/5d8bee01e28e464c94fa206d927d26f6\" \/>\n<meta name=\"author\" content=\"Andr\u00e9 Minhorst\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Andr\u00e9 Minhorst\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"25\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/RDBMSZugriff_per_VBA_Daten_bearbeiten\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/RDBMSZugriff_per_VBA_Daten_bearbeiten\\\/\"},\"author\":{\"name\":\"Andr\u00e9 Minhorst\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\"},\"headline\":\"RDBMS-Zugriff per VBA: Daten bearbeiten\",\"datePublished\":\"2020-05-14T13:41:20+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/RDBMSZugriff_per_VBA_Daten_bearbeiten\\\/\"},\"wordCount\":4008,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/RDBMSZugriff_per_VBA_Daten_bearbeiten\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg09.met.vgwort.de\\\/na\\\/5d8bee01e28e464c94fa206d927d26f6\",\"articleSection\":[\"1\\\/2017\",\"2017\",\"SQL Server und Co.\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/RDBMSZugriff_per_VBA_Daten_bearbeiten\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/RDBMSZugriff_per_VBA_Daten_bearbeiten\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/RDBMSZugriff_per_VBA_Daten_bearbeiten\\\/\",\"name\":\"RDBMS-Zugriff per VBA: Daten bearbeiten - Access im Unternehmen\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/RDBMSZugriff_per_VBA_Daten_bearbeiten\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/RDBMSZugriff_per_VBA_Daten_bearbeiten\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg09.met.vgwort.de\\\/na\\\/5d8bee01e28e464c94fa206d927d26f6\",\"datePublished\":\"2020-05-14T13:41:20+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/RDBMSZugriff_per_VBA_Daten_bearbeiten\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/RDBMSZugriff_per_VBA_Daten_bearbeiten\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/RDBMSZugriff_per_VBA_Daten_bearbeiten\\\/#primaryimage\",\"url\":\"http:\\\/\\\/vg09.met.vgwort.de\\\/na\\\/5d8bee01e28e464c94fa206d927d26f6\",\"contentUrl\":\"http:\\\/\\\/vg09.met.vgwort.de\\\/na\\\/5d8bee01e28e464c94fa206d927d26f6\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/RDBMSZugriff_per_VBA_Daten_bearbeiten\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"RDBMS-Zugriff per VBA: Daten bearbeiten\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\",\"name\":\"Access im Unternehmen\",\"description\":\"Das Magazin f\u00fcr Datenbankentwickler auf Basis von Microsoft Access\",\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/access-im-unternehmen.de\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"de\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\",\"name\":\"Andr\u00e9 Minhorst Verlag\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/wp-content\\\/uploads\\\/2019\\\/09\\\/aiu_wp.png\",\"contentUrl\":\"https:\\\/\\\/access-im-unternehmen.de\\\/wp-content\\\/uploads\\\/2019\\\/09\\\/aiu_wp.png\",\"width\":370,\"height\":111,\"caption\":\"Andr\u00e9 Minhorst Verlag\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\",\"name\":\"Andr\u00e9 Minhorst\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"caption\":\"Andr\u00e9 Minhorst\"}}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"RDBMS-Zugriff per VBA: Daten bearbeiten - Access im Unternehmen","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_bearbeiten\/","og_locale":"de_DE","og_type":"article","og_title":"RDBMS-Zugriff per VBA: Daten bearbeiten","og_description":"Im Beitrag \"RDBMS-Zugriff per VBA: Verbindungen\" haben wir die Grundlage f&uuml;r den Zugriff auf SQL Server-Datenbanken geschaffen, \"RDBMS-Zugriff per VBA: Daten abfragen\" zeigt, wie Sie die Daten einer SQL Server-Datenbank ermitteln. Im vorliegenden Teil dieser Beitragsreihe erfahren Sie, wie Sie die Daten einer SQL Server-Datenbank bearbeiten.","og_url":"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_bearbeiten\/","og_site_name":"Access im Unternehmen","article_published_time":"2020-05-14T13:41:20+00:00","og_image":[{"url":"http:\/\/vg09.met.vgwort.de\/na\/5d8bee01e28e464c94fa206d927d26f6","type":"","width":"","height":""}],"author":"Andr\u00e9 Minhorst","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Andr\u00e9 Minhorst","Gesch\u00e4tzte Lesezeit":"25\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_bearbeiten\/#article","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_bearbeiten\/"},"author":{"name":"Andr\u00e9 Minhorst","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f"},"headline":"RDBMS-Zugriff per VBA: Daten bearbeiten","datePublished":"2020-05-14T13:41:20+00:00","mainEntityOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_bearbeiten\/"},"wordCount":4008,"commentCount":0,"publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_bearbeiten\/#primaryimage"},"thumbnailUrl":"http:\/\/vg09.met.vgwort.de\/na\/5d8bee01e28e464c94fa206d927d26f6","articleSection":["1\/2017","2017","SQL Server und Co."],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_bearbeiten\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_bearbeiten\/","url":"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_bearbeiten\/","name":"RDBMS-Zugriff per VBA: Daten bearbeiten - Access im Unternehmen","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_bearbeiten\/#primaryimage"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_bearbeiten\/#primaryimage"},"thumbnailUrl":"http:\/\/vg09.met.vgwort.de\/na\/5d8bee01e28e464c94fa206d927d26f6","datePublished":"2020-05-14T13:41:20+00:00","breadcrumb":{"@id":"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_bearbeiten\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_bearbeiten\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_bearbeiten\/#primaryimage","url":"http:\/\/vg09.met.vgwort.de\/na\/5d8bee01e28e464c94fa206d927d26f6","contentUrl":"http:\/\/vg09.met.vgwort.de\/na\/5d8bee01e28e464c94fa206d927d26f6"},{"@type":"BreadcrumbList","@id":"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_bearbeiten\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/access-im-unternehmen.de\/"},{"@type":"ListItem","position":2,"name":"RDBMS-Zugriff per VBA: Daten bearbeiten"}]},{"@type":"WebSite","@id":"https:\/\/access-im-unternehmen.de\/#website","url":"https:\/\/access-im-unternehmen.de\/","name":"Access im Unternehmen","description":"Das Magazin f\u00fcr Datenbankentwickler auf Basis von Microsoft Access","publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/access-im-unternehmen.de\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"de"},{"@type":"Organization","@id":"https:\/\/access-im-unternehmen.de\/#organization","name":"Andr\u00e9 Minhorst Verlag","url":"https:\/\/access-im-unternehmen.de\/","logo":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/logo\/image\/","url":"https:\/\/access-im-unternehmen.de\/wp-content\/uploads\/2019\/09\/aiu_wp.png","contentUrl":"https:\/\/access-im-unternehmen.de\/wp-content\/uploads\/2019\/09\/aiu_wp.png","width":370,"height":111,"caption":"Andr\u00e9 Minhorst Verlag"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f","name":"Andr\u00e9 Minhorst","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","caption":"Andr\u00e9 Minhorst"}}]}},"_links":{"self":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55001074","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/comments?post=55001074"}],"version-history":[{"count":0,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55001074\/revisions"}],"wp:attachment":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/media?parent=55001074"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/categories?post=55001074"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/tags?post=55001074"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}