{"id":55001062,"date":"2016-12-01T00:00:00","date_gmt":"2020-05-22T13:34:44","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=1062"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"RDBMSZugriff_per_VBA_Daten_abfragen","status":"publish","type":"post","link":"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_abfragen\/","title":{"rendered":"RDBMS-Zugriff per VBA: Daten abfragen"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg09.met.vgwort.de\/na\/4ea130ebb15e4690b50c0d5ddbe5603e\" 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. Nun gehen wir einen Schritt weiter: Wir wollen mit den dort beschriebenen Methoden etwa zum Zusammenstellen einer Verbindungszeichenfolge auf die Daten einer SQL Server-Datenbank zugreifen. Dabei lernen Sie eine Reihe interessanter Funktionen kennen, die den Zugriff deutlich vereinfachen und die auch noch &uuml;beraus performant sind.<\/b><\/p>\n<p>Der eingangs erw&auml;hnte Beitrag <b>RDBMS-Zugriff per VBA: Verbindungen <\/b>(<b>www.access-im-unternehmen.de\/1054<\/b>) hat die Werkzeuge daf&uuml;r geliefert, dass Sie Verbindungszeichenfolgen aus einer Tabelle zusammenstellen und diese zum Aufbau einer Verbindung nutzen k&ouml;nnen. Im vorliegenden Beitrag nutzen wir vor allem die mit der Funktion <b>Standardverbindungszeichenfolge <\/b>ermittelte Verbindungszeichenfolge, um auf die Tabellen der SQL Server-Datenbank zuzugreifen. Sie k&ouml;nnen aber nat&uuml;rlich auch eine manuell als String zusammengestellte Verbindungszeichenfolge nutzen.<\/p>\n<h2>Beispieldatenbank<\/h2>\n<p>Wir verwenden weiterhin die im ersten Teil der Beitragsreihe vorgestellte Datenbank <b>Suedsturm<\/b> auf Basis des LocalDB-Datenbanksystems. Sie k&ouml;nnen aber nat&uuml;rlich auch eine Datenbank &uuml;ber den SQL Server nutzen (s. Bild 1).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2016_06\/pic_1062_001.png\" alt=\"Die in diesem Beitrag verwendete Verbindungszeichenfolge im Formular\" width=\"549,6265\" height=\"387,6741\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Die in diesem Beitrag verwendete Verbindungszeichenfolge im Formular<\/span><\/b><\/p>\n<p>Die Verkn&uuml;pfung zu den Tabellen der SQL Server-Datenbank erstellen Sie am einfachsten mit dem Formular <b>frmTabellenVerknuepfen<\/b>, das wir im Beitrag <b>SQL Server-Tools <\/b>(<b>www.access-im-unternehmen.de\/1061<\/b>) vorstellen.<\/p>\n<h2>Recordsets auf Basis von SQL Server-Daten<\/h2>\n<p>Recordsets sind unter Access ein h&auml;ufig genutztes Mittel f&uuml;r den Zugriff auf die Daten einer Tabelle oder Abfrage. Nat&uuml;rlich werden Sie diese auch f&uuml;llen wollen, wenn die Daten Ihrer Datenbank l&auml;ngst in einer SQL Server-Datenbank gelandet sind. Daher schauen wir uns nun zun&auml;chst an, wie Sie Recordsets auf der Basis verschiedener Zugriffsarten auf die Daten der SQL Server-Datenbank erstellen und f&uuml;llen k&ouml;nnen.<\/p>\n<p>Dabei greifen wir auf einfachem Wege auf die Daten zu, n&auml;mlich &uuml;ber eine verkn&uuml;pfte Tabelle, aber auch auf subtilere Weise &#8211; etwa &uuml;ber eine Pass-Through-Abfrage, die sich einer gespeicherten Prozedur als Datenquelle bedient.<\/p>\n<h2>Recordset auf Basis einer verkn&uuml;pften Tabelle<\/h2>\n<p>Die einfachste M&ouml;glichkeit, per VBA auf die Daten einer verkn&uuml;pften Tabelle zuzugreifen, ist das DAO-Recordset. Dieses erhalten Sie mit der <b>OpenRecordset<\/b>-Methode, der Sie als ersten Parameter den Namen der verkn&uuml;pften Tabelle, als zweiten den Wert <b>dbOpenSnapshot <\/b>(Daten &auml;ndern wollen wir nicht per verkn&uuml;pfter Tabelle, da zu unperformant &#8211; ein rein lesender Zugriff sorgt au&szlig;erdem f&uuml;r wesentlich weniger Sperren auf dem Server) und als dritten Parameter den Wert <b>dbSeeChanges<\/b>, um Fehler in Zusammenhang mit Autowerten in der zugrunde liegenden Tabelle zu vermeiden.<\/p>\n<p>Die folgende Beispielprozedur erstellt ein Recordset auf Basis der Tabelle <b>tblArtikel <\/b>und gibt die Werte der Felder <b>ArtikelID <\/b>und <b>Artikelname <\/b>aller Datens&auml;tze dieser Tabelle im Direktfenster aus:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>VerknuepfteTabellePerRecordset()\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 tblArtikel\", _\r\n         dbOpenSnapshot, dbSeeChanges)\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rst.EOF\r\n         <span style=\"color:blue;\">Debug.Print<\/span> rst!ArtikelID, rst!Artikelname\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     <span style=\"color:blue;\">Set<\/span> db = Nothing\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Wenn Sie die Daten aus der SQL Server-Tabelle nicht nur lesen, sondern auch &auml;ndern wollen, verwenden Sie <b>db-OpenDynaset <\/b>statt <b>dbOpenSnapshot<\/b>.<\/p>\n<h2>Vereinfachungen f&uuml;r den Zugriff auf gespeicherte Prozeduren<\/h2>\n<p>Gespeicherte Prozeduren sind prinzipiell Abfragen, die Auswahl- oder Aktionsabfragen enthalten und direkt auf dem SQL Server gespeichert sind und dort ausgef&uuml;hrt werden. Sie liefern nur die ben&ouml;tigten Daten zur&uuml;ck und sind daher meist viel schneller, als wenn Sie etwa mit verkn&uuml;pften Tabellen arbeiten.<\/p>\n<p>Hinweis: Die ab jetzt vorgestellten Techniken erlauben lediglich den lesenden Zugriff auf die ermittelten Daten.<\/p>\n<p>Wenn Sie ein Recordset auf Basis des Ergebnisses einer gespeicherten Prozedur verwenden m&ouml;chten, ben&ouml;tigen Sie folgende Dinge:<\/p>\n<ul>\n<li>die gespeicherte Prozedur in der SQL Server-Datenbank,<\/li>\n<li>eine Pass-Through-Abfrage in der Access-Datenbank und<\/li>\n<li>VBA-Code, der auf die Pass-Through-Abfrage zugreift.<\/li>\n<\/ul>\n<p>Im einfachsten Fall handelt es sich um eine gespeicherte Prozedur, die keine Parameter erwartet. Warum ist dies so unkompliziert Dies liegt in der Natur des Aufrufs einer gespeicherten Prozedur &uuml;ber eine Pass-Through-Abfrage begr&uuml;ndet.<\/p>\n<p>Die gespeicherte Prozedur liegt auf dem SQL Server und wird &uuml;ber eine Pass-Through-Abfrage mit der <b>EXEC<\/b>-Anweisung aufgerufen:<\/p>\n<pre>EXEC cbo.spBeispielprozedur<\/pre>\n<p>Kein Problem &#8211; die Pass-Through-Abfrage kann wie gesehen verwendet werden. Wenn Sie jedoch einen Parameter &uuml;bergeben m&uuml;ssen, geh&ouml;rt dieser in den SQL-Text der Pass-Through-Abfrage &#8211; also beispielsweise so:<\/p>\n<pre>EXEC dbo.spBeispielProzedurMitParameter ''''Beispielparameter''''<\/pre>\n<p><b>Beispielparameter <\/b>ist aber nicht bei jedem Aufruf gleich, sonst brauchten Sie ja keinen Parameter. Es wird also immer ein anderer Wert &uuml;bergeben &#8211; die <b>ID <\/b>eines zu l&ouml;schenden Datensatzes, das Vergleichskriterium f&uuml;r eine <b>SELECT<\/b>-Abfrage et cetera.<\/p>\n<p>Das bedeutet, dass Sie den SQL-Text der Abfrage mit jedem Aufruf neu erstellen m&uuml;ssen.<\/p>\n<p>Als weiteres Kriterium kommt hinzu, dass sich die Verbindungszeichenfolge bei der Arbeit mit einer Kombination aus Access-Frontend und SQL Server-Backend &auml;ndern kann &#8211; sei es, weil sich der Name des Servers, der Name der Datenbank, die Authentifizierungsmethode oder der zu verwendende Treiber &auml;ndert. Die Verbindungszeichenfolge wird an vielen Stellen verwendet, vor allem aber als Eigenschaft der Pass-Through-Abfragen, die sich im Laufe der Entwicklung einer Access-Anwendung mit SQL Server-Backend ansammeln werden.<\/p>\n<p>Aber brauchen Sie all diese Pass-Through-Abfragen &uuml;berhaupt Letztlich m&uuml;ssen die meisten ohnehin jeweils mit einem neuen SQL-Ausdruck gef&uuml;llt werden, da diese etwa verschiedene Parameter verwenden. Warum also den Navigationsbereich mit hunderten von Pass-Through-Abfragen f&uuml;llen, wenn man diese auch jeweils tempor&auml;r erzeugen kann Die Antwort ist: Irgendwo m&uuml;ssen wir ja den Code f&uuml;r den Zugriff auf die gespeicherten Abfragen speichern. Dazu bietet sich jeweils eine Pass-Through-Abfrage je gespeicherter Abfrage durchaus an.<\/p>\n<p>Wir verwenden aber sp&auml;ter dennoch einen Satz von VBA-Funktionen und -Prozeduren, welche die jeweiligen Pass-Through-Abfragen untersuchen, den enthaltenen T-SQL-Code zum Aufruf der gespeicherten Prozedur entnehmen und die Pass-Through-Abfrage auf Basis der zu &uuml;bergebenden Parameterwerte und der Verbindungszeichenfolge neu erzeugen und das gew&uuml;nschte Objekt zur&uuml;ckliefern. Denn: F&uuml;r den Zugriff auf eine gespeicherte Prozedur ben&ouml;tigen wir gar keine spezielle gespeicherte Pass-Through-Abfrage, sondern lediglich eine zur Laufzeit erstellte Abfrage, die wir mit dem Schl&uuml;sselwort <b>EXEC<\/b>, dem Namen der gespeicherten Prozedur sowie den gegebenenfalls ben&ouml;tigten Parameterwerten f&uuml;llen.<\/p>\n<p>In den folgenden Abschnitten sehen wir uns an, wie Sie den Zugriff auf gespeicherte Prozeduren per VBA vereinfachen k&ouml;nnen. Insgesamt stellen wir dort die folgenden Prozeduren vor, die sich allesamt im Modul <b>mdlToolsSQLServer <\/b>befinden:<\/p>\n<ul>\n<li>Ausf&uuml;hren einer gespeicherten Prozedur ohne Parameter und R&uuml;ckgabe des Ergebnisses als Recordset<\/li>\n<li>Ausf&uuml;hren einer gespeicherten Prozedur mit Parameter und R&uuml;ckgabe des Ergebnisses als Recordset<\/li>\n<li>Erstellen einer Pass-Through-Abfrage und R&uuml;ckgabe des Namens der Abfrage, etwa als Wert der Eigenschaft <b>RecordSource <\/b>(Formulare, Berichte) oder <b>RowSource <\/b>(Kombinationsfeld, Lis-ten-feld), ohne Parameter<\/li>\n<li>Erstellen derselben Pass-Through-Abfrage, diesmal mit der &uuml;bergabe von Parametern<\/li>\n<\/ul>\n<h2>Recordset aus gespeicherter Prozedur<\/h2>\n<p>Die n&auml;chste Variante, per Recordset auf die Daten einer SQL Server-Tabelle zuzugreifen, ist der Zugriff auf eine gespeicherte Prozedur (<b>Stored Procedure<\/b>) &uuml;ber eine Pass-Through-Abfrage.<\/p>\n<p>Eine solche Pass-Through-Abfrage m&uuml;ssen Sie zun&auml;chst einmal erstellen. Da wir davon ausgehen, dass Sie neben LocalDB und\/oder SQL Server auch das SQL Server Managment Studio installiert haben, erstellen wir die neue gespeicherte Prozedur einfach von dort aus.<\/p>\n<p>Dazu starten Sie das SQL Server Management Studio, verbinden sich mit der entsprechenden Instanz (in unserem Fall mit <b>(localdb)\\MSSQLLocalDB<\/b>) und wechseln zur Datenbank <b>Suedsturm<\/b>.<\/p>\n<h2>Gespeicherte Prozedur erstellen<\/h2>\n<p>Hier &ouml;ffnen Sie das Datenbank-Element <b>Suedsturm <\/b>und darunter die Elemente <b>Programmierbarkeit <\/b>und <b>Gespeicherte Prozeduren<\/b>. Letzteres Element bietet im Kontextmen&uuml; den Eintrag <b>Gespeicherte Prozedur <\/b>an, mit der Sie eine neue Prozedur erstellen k&ouml;nnen (s. Bild 2).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2016_06\/pic_1062_002.png\" alt=\"Anlegen einer gespeicherten Prozedur\" width=\"499,6607\" height=\"403,6113\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Anlegen einer gespeicherten Prozedur<\/span><\/b><\/p>\n<p>Sie k&ouml;nnen dies allerdings auch direkt &uuml;ber eine neue Abfrage erledigen, die Sie &uuml;ber den Kontextmen&uuml;-Eintrag <b>Neue Abfrage <\/b>des <b>Suedsturm<\/b>-Elements &ouml;ffnen. Hier tragen Sie die T-SQL-Anweisung ein, mit der Sie die gespeicherte Abfrage erstellen und die wie folgt aussieht:<\/p>\n<pre>CREATE PROC dbo.pAlleArtikel\r\nAS\r\nSELECT dbo.tblArtikel.ArtikelID, \r\n     dbo.tblArtikel.Artikelname, \r\n     dbo.tblArtikel.AufgenommenAm, \r\n     dbo.tblArtikel.Auslaufartikel, \r\n     dbo.tblArtikel.BestellteEinheiten, \r\n     dbo.tblArtikel.Einzelpreis, \r\n     dbo.tblArtikel.KategorieID, \r\n     dbo.tblArtikel.Lagerbestand, \r\n     dbo.tblArtikel.LieferantID, \r\n     dbo.tblArtikel.Liefereinheit, \r\n     dbo.tblArtikel.Mindestbestand \r\nFROM dbo.tblArtikel;<\/pre>\n<p>Danach f&uuml;hren Sie die Abfrage mit der Taste <b>F5 <\/b>aus (s. Bild 3). Sie finden nun nach einer Aktualisierung einen neuen Eintrag namens <b>pAlleArtikel <\/b>unter dem Element <b>Suedsturm|Pro-gram-mierbarkeit|Gespeicherte Prozeduren<\/b>.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2016_06\/pic_1062_003.png\" alt=\"Testen einer gespeicherten Prozedur\" width=\"499,6607\" height=\"321,4871\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 3: Testen einer gespeicherten Prozedur<\/span><\/b><\/p>\n<p>Sie k&ouml;nnen diese testen, indem Sie in einem Abfragefenster die Anweisung <b>EXEC pAlleArtikel; <\/b>eingeben und diese mit <b>F5 <\/b>ausf&uuml;hren.<\/p>\n<h2>Pass-Through-Abfrage erstellen<\/h2>\n<p>Nun erstellen wir in der Access-Datenbank eine Pass-Through-Abfrage, mit der wir &uuml;ber die gespeicherte Prozedur auf die Daten der Tabelle <b>tblArtikel <\/b>zugreifen wollen.<\/p>\n<p>Legen Sie dazu eine neue, leere Abfrage in der Entwurfsansicht an und schlie&szlig;en Sie den automatisch erscheinenden Dialog <b>Tabelle anzeigen<\/b>. Nun klicken Sie auf den Ribbon-Eintrag <b>Entwurf|Abfragetyp|Pass-Through <\/b>(s. Bild 4).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2016_06\/pic_1062_004.png\" alt=\"Umwandeln einer Abfrage in eine Pass-Through-Abfrage\" width=\"649,559\" height=\"424,4323\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 4: Umwandeln einer Abfrage in eine Pass-Through-Abfrage<\/span><\/b><\/p>\n<p>Dies &auml;ndert vor allem die Ansicht der Abfrage, n&auml;mlich in die SQL-Ansicht. Hier sind nun zwei Schritte zu erledigen: Als Erstes geben Sie die Anweisung ein, mit der Sie zuvor schon die gespeicherte Prozedur im SQL Server Management Studio getestet haben, also <b>EXEC dbo.pAlleArtikel<\/b>. Au&szlig;erdem m&uuml;ssen Sie der Abfrage noch mitteilen, woher sie ihre Daten beziehen soll. Dies erledigen wir per Hand &uuml;ber die Eigenschaft ODBC-Verbindung, der wir in unserem Beispiel die folgende Zeichenkette &uuml;bergeben:<\/p>\n<pre>ODBC;DRIVER={ODBC Driver 11 for SQL Server};SERVER=(localdb)\\MSSQLLocalDB;DATABASE=Suedsturm;Trusted_Connection=Yes<\/pre>\n<p>Schlie&szlig;lich muss noch die Eigenschaft <b>Liefert Datens&auml;tze <\/b>auf den Wert <b>Ja <\/b>eingestellt sein (s. Bild 5).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2016_06\/pic_1062_006.png\" alt=\"Eigenschaften der Pass-Through-Abfrage\" width=\"700\" height=\"182,7822\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 5: Eigenschaften der Pass-Through-Abfrage<\/span><\/b><\/p>\n<p>Anschlie&szlig;end k&ouml;nnen Sie die Datenblattansicht dieser Abfrage aktivieren und erhalten das gew&uuml;nschte Ergebnis (s. Bild 6).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2016_06\/pic_1062_005.png\" alt=\"Ergebnis der Pass-Through-Abfrage\" width=\"649,559\" height=\"311,0697\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 6: Ergebnis der Pass-Through-Abfrage<\/span><\/b><\/p>\n<h2>Per Recordset auf gespeicherte Prozedur zugreifen<\/h2>\n<p>Nun wollen wir auch per VBA &uuml;ber die Pass-Through-Abfrage auf die von der gespeicherten Prozedur gelieferten Daten zugreifen.<\/p>\n<p>Auf diese Pass-Through-Abfrage greifen Sie wie folgt zu und durchlaufen dann ihre Datens&auml;tze &uuml;ber das zuvor gef&uuml;llte <b>Recordset<\/b>-Objekt:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>GespeicherteProzedurPerRecordset()\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(\"ptSELECTAlleArtikel\")\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rst.EOF\r\n         <span style=\"color:blue;\">Debug.Print<\/span> rst!ArtikelID, rst!Artikelname\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     <span style=\"color:blue;\">Set<\/span> db = Nothing\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Hier geschieht rein oberfl&auml;chlich nicht viel anderes, als wenn Sie wie weiter oben auf eine verkn&uuml;pfte Tabelle zugreifen &#8211; mit dem Unterschied, dass wir hier den Namen der Pass-Through-Abfrage der <b>OpenRecordset<\/b>-Methode verwenden.<\/p>\n<p>Vorbereitend auf weitere Varianten, in denen wir beispielsweise Parameter verwenden wollen, die in den Entwurf der Abfragen einflie&szlig;en m&uuml;ssen, schauen wir uns nun eine Alternative an, bei der wir zun&auml;chst ein <b>QueryDef<\/b>-Objekt auf Basis der Pass-Through-Abfrage <b>ptSELECTAlleArtikel <\/b>erstellen und erst dann mit der <b>OpenRecordset<\/b>-Methode auf diesem Objekt ein Recordset erstellen:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>GespeicherteProzedurPerQueryDef()\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.QueryDefs(\"ptSELECTAlleArtikel\")\r\n     <span style=\"color:blue;\">Set<\/span> rst = qdf.OpenRecordset()\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rst.EOF\r\n         <span style=\"color:blue;\">Debug.Print<\/span> rst!ArtikelID, rst!Artikelname\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     <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<h2>Was geschieht bei ge&auml;nderter Verbindungszeichenfolge<\/h2>\n<p>Wenn Sie die Anwendung auf Ihrem Rechner entwickeln und diese dann beispielsweise an den Kunden weitergeben, werden Sie voraussichtlich eine andere Verbindungszeichenfolge nutzen m&uuml;ssen. <\/p>\n<p>In diesem Fall haben Sie ein Problem: Wir haben ja weiter oben beschrieben, wie Sie einer Pass-Through-Abfrage die Verbindungszeichenfolge mitteilen &#8211; und zwar durch direktes Eintragen dieser Zeichenfolge in die Eigenschaft <b>ODBC-Verbindung<\/b>. Wenn Sie nun eine ganze Reihe dieser Pass-Through-Abfragen nutzen, m&uuml;ssten Sie diese vor jeder Weitergabe an andere Anwender &auml;ndern. Diese Arbeit wollen Sie nicht wirklich erledigen, also automatisieren wir dies. Dazu erstellen wir eine Prozedur namens <b>PTAbfrageAktualisieren<\/b>, die wie in Listing 1 aussieht.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>PTAbfrageAktualisieren(strPTAbfrage<span style=\"color:blue;\"> As String<\/span>, strVerbindungszeichenfolge<span style=\"color:blue;\"> As String<\/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>intStart<span style=\"color:blue;\"> As Integer<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>intEnde<span style=\"color:blue;\"> As Integer<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strVerbindungOhnePWD<span style=\"color:blue;\"> As String<\/span>\r\n     intStart = <span style=\"color:blue;\">InStr<\/span>(1, strVerbindungszeichenfolge, \";PWD\")\r\n     <span style=\"color:blue;\">If <\/span>intStart &gt; 0<span style=\"color:blue;\"> Then<\/span>\r\n         strVerbindungOhnePWD = <span style=\"color:blue;\">Left<\/span>(strVerbindungszeichenfolge, intStart)\r\n         intEnde = <span style=\"color:blue;\">InStr<\/span>(intStart + 1, strVerbindungszeichenfolge, \";\")\r\n         <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> intEnde = 0<span style=\"color:blue;\"> Then<\/span>\r\n             strVerbindungOhnePWD = strVerbindungOhnePWD & <span style=\"color:blue;\">Mid<\/span>(strVerbindungszeichenfolge, intEnde + 1)\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         strVerbindungOhnePWD = strVerbindungszeichenfolge\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> qdf = db.QueryDefs(strPTAbfrage)\r\n     qdf.Connect = strVerbindungOhnePWD\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Prozedur zum Aktualisieren von Pass-Through-Abfragen<\/span><\/b><\/p>\n<p>Die Funktion erwartet den Namen der Pass-Through-Abfrage und die Verbindungszeichenfolge als Parameter. Da diese den weiter oben vorgestellten Techniken zufolge mit der Funktion <b>VerbindungszeichenfolgeNachID <\/b>oder <b>Standardverbindungszeichenfolge <\/b>zusammengestellt wird, enth&auml;lt sie bei Benutzung der SQL Server-Authentifizierung gegebenenfalls das Kennwort des aktuellen Be-nut-zers.<\/p>\n<p>Daher enth&auml;lt die Funktion einige Zeilen Code, welche die Verbindungszeichenfolge auf das Auf-treten des Ausdrucks <b>;PWD= <\/b>untersuchen. Ist dieser vorhanden, wird eine neue Ver-bin-dungs-zeichenfolge namens <b>strVerbindungOhne-PWD <\/b>zusammengestellt, die alle Zeichen bis zum Auf-tre-ten von <b>;PWD= <\/b>enth&auml;lt und gegebenenfalls alle Zeichen hinter dem nachfolgenden Semi-ko-lon.<\/p>\n<p><!--30percent--><\/p>\n<p>Dazu ermittelt die Prozedur zun&auml;chst mit der <b>InStr<\/b>-Funktion die Position des ersten Auftretens der Zeichenfolge <b>;PWD<\/b>. Ist diese ungleich <b>0<\/b>, enth&auml;lt die Zeichenfolge dieses Element. Eine neue <b>String<\/b>-Variable namens <b>strVerbindungOhnePWD <\/b>nimmt dann zun&auml;chst den Inhalt der Verbindungszeichenfolge bis zum Beginn von <b>;PWD <\/b>auf. Dann ermittelt sie die Position des ersten Semikolons hinter der Zeichenfolge <b>;PWD<\/b>, also das Ende des entsprechenden Name-Wert-Paares.<\/p>\n<p>Hat das Ergebnis des entsprechenden Aufrufs der <b>InStr<\/b>-Funktion den Wert <b>0<\/b>, ist die Angabe von <b>PWD <\/b>das letzte Element der Verbindungszeichenfolge und es sind keine weiteren Schritte n&ouml;tig &#8211; die Verbindungszeichenfolge exklusive <b>PWD<\/b>-Element befindet sich nun in der Variablen <b>strVerbindungOhnePWD<\/b>. Anderenfalls gibt es noch mindestens ein weiteres Element hinter dem Name-Wert-Paar mit dem Kennwort &#8211; also wird der komplette Rest hinter der ermittelten Position an den bisherigen Inhalt von <b>strVerbindungOhnePWD <\/b>angeh&auml;ngt.<\/p>\n<p>Nachdem das PWD-Element aus der Verbindungszeichenfolge entfernt und in der Variablen <b>strVerbindungOhnePWD <\/b>gespeichert wurde, referenziert die Prozedur <b>PTAbfrageAktualisieren <\/b>die zugrunde liegende Pass-Through-Abfrage und stellt die Verbindungszeichenfolge mit der <b>Connect<\/b>-Eigenschaft auf den neuen Wert ein.<\/p>\n<p>Die Beispieldatenbank enth&auml;lt beispielsweise in der Tabelle <b>tblVerbindungszeichenfolgen <\/b>zwei Beispielverbindungen mit den ID-Werten <b>9 <\/b>und <b>10<\/b>. Wenn Sie die erste davon verwenden wollen, rufen Sie die Prozedur wie folgt auf:<\/p>\n<pre>PTAbfrageAktualisieren \"PTSELECTAlleArtikel\",  VerbindungszeichenfolgeNachID(9)<\/pre>\n<p>Wollen Sie dann wechseln, nutzen Sie die andere ID:<\/p>\n<pre>PTAbfrageAktualisieren \"PTSELECTAlleArtikel\",  VerbindungszeichenfolgeNachID(10)<\/pre>\n<p>Sie k&ouml;nnen nat&uuml;rlich auch direkt die als Standardverbindungszeichenfolge deklarierte Verbindungszeichenfolge als Parameter angeben:<\/p>\n<pre>PTAbfrageAktualisieren \"PTSELECTAlleArtikel\",  Standardverbindungszeichenfolge<\/pre>\n<p>Sie k&ouml;nnen sich zwischendurch den Entwurf der Pass-Through-Abfrage ansehen &#8211; die Eigenschaft <b>ODBC-Verbindung <\/b>enth&auml;lt jeweils die entsprechende Verbindungszeichenfolge.<\/p>\n<h2>Alle Pass-Through-Abfragen aktualisieren<\/h2>\n<p>Wenn Sie mit dieser Prozedur alle Pass-Through-Abfragen aktualisieren wollen, durchlaufen Sie in einer weiteren Prozedur alle Eintr&auml;ge der Tabelle <b>MSysObjects<\/b>, deren Feld <b>Connect <\/b>einen Wert enth&auml;lt, der mit <b>ODBC= <\/b>beginnt, und rufen von dort f&uuml;r jeden Eintrag die Prozedur <b>PT-Ab-frageAktualisieren <\/b>auf (s. Listing 2).<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>AlleAbfragenAktualisieren()\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     For Each qdf In db.QueryDefs\r\n         <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">InStr<\/span>(Nz(qdf.Connect,\"\"), \"ODBC;\") &gt; 0<span style=\"color:blue;\"> Then<\/span>\r\n             PTAbfrageAktualisieren qdf.Name, Standardverbindungszeichenfolge\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">Next<\/span> qdf\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 2: Prozedur zum Aktualisieren aller Pass-Through-Abfragen mit der aktuellen Standardverbindungszeichenfolge<\/span><\/b><\/p>\n<h2>Wann aktualisieren<\/h2>\n<p>Es stellt sich die Frage, wann man etwa die Verbindungszeichenfolge von Pass-Through-Abfragen aktualisiert. Die performanteste L&ouml;sung w&auml;re wohl, wenn man dies nur dann durchf&uuml;hrt, wenn sich die Standardverbindungszeichenfolge der Anwendung &auml;ndert. Wenn Sie also etwa die Instanz der Anwendung auf Ihrem Rechner auf die Weitergabe zum Kunden vorbereiten, k&ouml;nnten Sie einfach die Standardverbindungszeichenfolge &auml;ndern (etwa mithilfe des Formulars <b>frmVerbindungszeichenfolgen<\/b>, das wir im Beitrag <b>SQL Server-Tools<\/b>, <b>www.access-im-unternehmen.de\/1061<\/b>, vorstellen) und danach die Prozedur <b>AlleAbfragenAktualisieren <\/b>aufrufen.<\/p>\n<p>Gegebenenfalls f&uuml;gen Sie den Aufruf dieser Prozedur sogar zu der Prozedur hinzu, die im Formular die aktuelle Verbindung als Standardverbindung einstellt.<\/p>\n<p>Sie k&ouml;nnten die jeweilige Pass-Through-Abfrage auch immer dann aktualisieren, wenn Sie &uuml;berhaupt ein Recordset auf Basis einer solchen Abfrage erstellen. Wenn Sie gespeicherte Prozeduren mit Parametern verwenden, m&uuml;ssen Sie die Abfrage ohnehin aktualisieren &#8211; dann k&ouml;nnen Sie auch gleich die Verbindungszeichenfolge mit hinzu nehmen.<\/p>\n<h2>Recordset-Funktionen<\/h2>\n<p>Da Sie gegebenenfalls regelm&auml;&szlig;ig Recordsets f&uuml;r die verschiedenen Anforderungen ben&ouml;tigen, stellen wir dazu geeignete Funktionen zusammen. Diese k&ouml;nnen Sie dann beispielsweise einsetzen, wenn Sie Daten per VBA durchlaufen wollen, aber Sie k&ouml;nnen diese auch der Recordset-Eigenschaft von Formularen, Berichten, Kombinationsfeldern oder Listenfeldern zuweisen. Wir stellen zwei verschiedene Funktionen vor:<\/p>\n<ul>\n<li><b>SPRecordset<\/b>: Liefert ein einfaches Recordset ohne Einsatz von Parametern<\/li>\n<li><b>SPRecordsetMItParameter<\/b>: Liefert ein Recordset auf Basis einer gespeicherten Prozedur mit Parametern.<\/li>\n<\/ul>\n<h2>Recordset aus gespeicherter Prozedur ohne Parameter<\/h2>\n<p>Die Funktion <b>SPRecordset <\/b>soll ein Recordset basierend auf der per Funktionsparameter angegebenen gespeicherten Prozedur f&uuml;r eine ebenfalls per Funktionsparameter &uuml;bergebene Verbindung zur&uuml;ckliefern.<\/p>\n<p>Um das Ergebnis an ein <b>Recordset<\/b>-Objekt zu &uuml;bergeben, ruft die Prozedur die <b>OpenRecordset<\/b>-Methode f&uuml;r die tempor&auml;re Pass-Through-Abfrage mit der gespeicherten Prozedur auf. Diese Variante arbeitet mit gespeicherten Prozeduren, die keine Parameter erwarten (s. Listing 3).<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>SPRecordset(strStoredProcedure<span style=\"color:blue;\"> As String<\/span>, strVerbindungszeichenfolge<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As <\/span>DAO.Recordset\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.CreateQueryDef(\"\")\r\n     <span style=\"color:blue;\">With<\/span> qdf\r\n         .Connect = strVerbindungszeichenfolge\r\n         .SQL = \"EXEC \" & strStoredProcedure\r\n         <span style=\"color:blue;\">Set<\/span> SPRecordset = .OpenRecordset\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 3: Diese Funktion liefert ein Recordset basierend auf einer gespeicherten Prozedur und einer entsprechenden Verbindungszeichenfolge.<\/span><\/b><\/p>\n<p>Wenn Sie die Funktion ausprobieren und das zur&uuml;ckgelieferte <b>Recordset<\/b>-Objekt weiterverwenden m&ouml;chten, k&ouml;nnen Sie dies mit einer Prozedur wie der aus Listing 4 erledigen. Diese deklariert ihrerseits ein <b>Recordset<\/b>-Objekt und weist diesem das Ergebnis der Funktion <b>SPRecordset <\/b>zu. Anschlie&szlig;end durchl&auml;uft die Prozedur noch die Datens&auml;tze des zur&uuml;ckgelieferten Recordsets.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>TestSPRecordset()\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> rst = SPRecordset(\"dbo.spSELECTAlleWarengruppen\", Standardverbindungszeichenfolge)\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rst.EOF\r\n         <span style=\"color:blue;\">Debug.Print<\/span> rst!WarengruppeID\r\n         rst.Move<span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 4: Test der Funktion zum Holen eines Recordsets auf Basis einer gespeicherten Prozedur<\/span><\/b><\/p>\n<p>Sie k&ouml;nnen die Funktion auch innerhalb eines Formulars testen. Dazu versehen Sie ein leeres Formular zun&auml;chst mit der verkn&uuml;pften Tabelle als Datenherkunft. Damit k&ouml;nnen Sie dann die gebundenen Felder ganz einfach aus der Feldliste in das Formular ziehen (s. Bild 7). Das Formular w&uuml;rde nun beim Wechseln in die Formularansicht die Daten der Tabelle <b>tblArtikel <\/b>anzeigen &#8211; allerdings aus der verkn&uuml;pften Tabelle. Wir wollen aber die Daten aus der gespeicherten Prozedur liefern. Dazu f&uuml;gen wir dem Formular eine Ereignisprozedur hinzu, die durch das Ereignis <b>Beim Laden <\/b>ausgel&ouml;st wird:<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2016_06\/pic_1062_007.png\" alt=\"Ausstatten des Formulars mit gebundenen Feldern\" width=\"499,6607\" height=\"447,7593\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 7: Ausstatten des Formulars mit gebundenen Feldern<\/span><\/b><\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Load()\r\n     <span style=\"color:blue;\">Set<\/span> Me.Recordset = _\r\n         SPRecordset(\"pAlleArtikel\", _\r\n         Standardverbindungszeichenfolge)\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Die Prozedur ruft die Funktion <b>SPRecordset <\/b>mit den gew&uuml;nschten Parametern auf und &uuml;bergibt das gef&uuml;llte Recordset der gleichnamigen Eigenschaft des Formulars (siehe Beispielanwendung, Formular <b>frmAlleArtikel_Recordset<\/b>). Dieses zeigt dann beim &ouml;ffnen gleich die Daten aus der zugrunde liegenden gespeicherten Prozedur an (s. Bild 8).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2016_06\/pic_1062_008.png\" alt=\"Formular mit Daten aus einer gespeicherten Prozedur\" width=\"424,7115\" height=\"316,5875\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 8: Formular mit Daten aus einer gespeicherten Prozedur<\/span><\/b><\/p>\n<h2>Recordset aus gespeicherter Prozedur mit Parameter<\/h2>\n<p>Die zweite Variante dieser Prozedur erwartet zus&auml;tzlich die &uuml;bergabe eines oder mehrerer Pa-ra-meter in einem <b>ParamArray<\/b>. Dieses soll wiederum die f&uuml;r die gespeicherte Prozedur definierten Parameter abdecken. Bevor wir uns die Funktion ansehen, wollen wir erst einmal eine gespeicherte Prozedur mit Parametern erstellen. Wir wollen damit alle Artikel einer bestimmten Kategorie zur&uuml;ckliefern. Das erledigen wir mit der folgenden T-SQL-Anweisung:<\/p>\n<pre>CREATE PROC dbo.pArtikelNachKategorieID (@KategorieID int) AS\r\nSELECT dbo.tblArtikel.ArtikelID, \r\n     dbo.tblArtikel.Artikelname, \r\n     dbo.tblArtikel.AufgenommenAm, \r\n     dbo.tblArtikel.Auslaufartikel, \r\n     dbo.tblArtikel.BestellteEinheiten, \r\n     dbo.tblArtikel.Einzelpreis, \r\n     dbo.tblArtikel.KategorieID, \r\n     dbo.tblArtikel.Lagerbestand, \r\n     dbo.tblArtikel.LieferantID, \r\n     dbo.tblArtikel.Liefereinheit, \r\n     dbo.tblArtikel.Mindestbestand \r\nFROM dbo.tblArtikel\r\nWHERE dbo.tblArtikel.KategorieID = @KategorieID;<\/pre>\n<p>Der Unterschied zur vorherigen gespeicherten Prozedur <b>pAlleArtikel <\/b>liegt erstens in dem in Klammern angegebenen Parameter <b>@KategorieID <\/b>und zweitens in der damit erstellten <b>WHERE<\/b>-Bedingung <b>dbo.tblArtikel.KategorieID = @KategorieID<\/b>. Ein beispielhafter Aufruf im SQL Server Management Studio k&ouml;nnte wie in Bild 9 aussehen &#8211; hier werden nur die Artikel der Kategorie mit der ID <b>1 <\/b>zur&uuml;ckgeliefert.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2016_06\/pic_1062_009.png\" alt=\"Testen einer gespeicherten Prozedur mit Parameter\" width=\"549,6265\" height=\"394,1122\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 9: Testen einer gespeicherten Prozedur mit Parameter<\/span><\/b><\/p>\n<p>Zur&uuml;ck zur Funktion <b>SPRecordsetMitParameter<\/b>, die drei Parameter erwartet (s. Listing 5):<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>SPRecordsetMitParameter(strStoredProcedure<span style=\"color:blue;\"> As String<\/span>, strVerbindungszeichenfolge<span style=\"color:blue;\"> As String<\/span>, _\r\n         ParamArray varParameter()<span style=\"color:blue;\"> As Variant<\/span>)<span style=\"color:blue;\"> As <\/span>DAO.Recordset\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         .SQL = \"EXEC \" & strStoredProcedure & \" \" & strParameter\r\n         <span style=\"color:blue;\">Set<\/span> SPRecordsetMitParameter = .OpenRecordset\r\n         <span style=\"color:blue;\">On Error GoTo<\/span> 0\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 5: Funktion zum Holen eines Recordsets auf Basis einer gespeicherten Prozedur mit Parametern<\/span><\/b><\/p>\n<ul>\n<li><b>strStoredProcedure<\/b>: Name der gespeicherten Prozedur,<\/li>\n<li><b>strVerbindungszeichenfolge<\/b>: Verbindungszeichenfolge und<\/li>\n<li><b>varParameter<\/b>: <b>ParamArray <\/b>mit einem oder mehreren Werten f&uuml;r die Parameter der gespeicherten Prozedur. Ein <b>ParamArray <\/b>ist eine Liste mehrerer per Komma getrennter Elemente, die unterschiedliche Datentypen haben k&ouml;nnen &#8211; also beispielsweise <b>String <\/b>oder Zahlendatentypen wie <b>Integer <\/b>oder <b>Long<\/b>.<\/li>\n<\/ul>\n<p>Die Funktion erstellt zun&auml;chst ein neues <b>QueryDef<\/b>-Objekt, wobei sie der <b>CreateQueryDef<\/b>-Methode eine leere Zeichenfolge &uuml;bergibt (<b>&#8222;&#8220;<\/b>) und das Ergebnis in der Variablen <b>qdf <\/b>unterbringt. Das hei&szlig;t, dass die Abfrage nicht als eigenes Objekt in der Datenbank gespeichert wird. Dann stellt sie mit einer Hilfsfunktion namens <b>Parameterliste <\/b>aus dem gelieferten <b>ParamArray <\/b>eine Liste von Werten ohne Kommata zusammen (mehr dazu gleich im Anschluss). Danach stellt sie einige Eigenschaften des neuen <b>QueryDef<\/b>-Objekts aus der Variablen <b>qdf <\/b>ein: <b>Connect <\/b>erh&auml;lt die Verbindungszeichenfolge aus dem Parameter <b>strVerbindungszeichenfolge <\/b>und <b>SQL <\/b>eine Zeichenkette, die aus dem Schl&uuml;sselwort <b>EXEC<\/b>, dem Namen der gespeicherten Prozedur aus der Variablen <b>strStoredProcedure <\/b>und der Parameterliste besteht. Dann f&uuml;hrt sie die <b>OpenRecordset<\/b>-Methode auf dem <b>QueryDef<\/b>-Objekt auf <b>qdf <\/b>aus und &uuml;bergibt das Ergebnis an den R&uuml;ckgabewert der Funktion.<\/p>\n<p>Ein Beispielaufruf sieht so wie in Listing 6 aus. Hier wird der Wert f&uuml;r den Parameter (hier <b>1 <\/b>als <b>KategorieID<\/b>) als einzelner Wert am Ende der Parameterliste &uuml;bergeben. Die folgende <b>Do While<\/b>-Schleife durchl&auml;uft alle gefundenen Datens&auml;tze und gibt den Inhalt einiger Felder im Direktfens-ter aus. In einem Formular s&auml;he dies &auml;nlich wie im vorherigen Beispiel aus. Hier verwenden wir dann allerdings die Funktion <b>SPRecordsetMitParameter <\/b>und geben den zus&auml;tzlichen Parameter noch mit an (siehe Beispieldatenbank, Formular <b>frmArtikelNachKategorieID_Recordset<\/b>):<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>Test_SPRecordsetMitParameter()\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> rst = SPRecordsetMitParameter(\"dbo.pArtikelNachKategorieID\", Standardverbindungszeichenfolge, 1)\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rst.EOF\r\n         <span style=\"color:blue;\">Debug.Print<\/span> rst!ArtikelID, rst!Artikelname, rst!KategorieID\r\n         rst.Move<span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 6: Beispielaufruf der Funktion SPRecordsetMitParameter<\/span><\/b><\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Load()\r\n     <span style=\"color:blue;\">Set<\/span> Me.Recordset = SPRecordsetMitParameter( _\r\n         \"pArtikelNachKategorieID\", _\r\n         Standardverbindungszeichenfolge, 1)\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Die Funktion &#8222;Parameterliste&#8220;<\/h2>\n<p>Diese Hilfsfunktion haben wir ausgegliedert, da sie gleich in mehreren Routinen zum Einsatz kommt (s. Listing 7). Sie erwartet ein Parameter-Array, also eine Liste von Variablen oder Ausdr&uuml;cken, die unterschiedlichen Datentyps sein k&ouml;nnen &#8211; beispielsweise Zeichenketten oder Zahlen.<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>Parameterliste(ByVal varParameter<span style=\"color:blue;\"> As Variant<\/span>)<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;\">Dim <\/span>strParameter<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>i<span style=\"color:blue;\"> As Integer<\/span>\r\n     For i = <span style=\"color:blue;\">LBound<\/span>(varParameter) To <span style=\"color:blue;\">UBound<\/span>(varParameter)\r\n         Select Case VarType(varParameter(i))\r\n             <span style=\"color:blue;\">Case <\/span>vbString\r\n                 <span style=\"color:blue;\">If <\/span>varParameter(i) = \"NULL\"<span style=\"color:blue;\"> Then<\/span>\r\n                     strParameter = strParameter & \", \" & varParameter(i)\r\n                 <span style=\"color:blue;\">Else<\/span>\r\n                     strParameter = strParameter & \", ''''\" & varParameter(i) & \"''''\"\r\n                 <span style=\"color:blue;\">End If<\/span>\r\n             <span style=\"color:blue;\">Case <\/span>0, 1\r\n                 strParameter = strParameter & \", NULL\"\r\n             <span style=\"color:blue;\">Case Else<\/span>\r\n                 strParameter = strParameter & \", \" & <span style=\"color:blue;\">Replace<\/span>(CStr(varParameter(i)), \",\", \".\")\r\n         <span style=\"color:blue;\">End Select<\/span>\r\n     <span style=\"color:blue;\">Next<\/span> i\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     Parameterliste = strParameter\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 7: Funktion zum Zusammenstellen von Parametern aus einem Parameter-Array<\/span><\/b><\/p>\n<p>Die Prozedur durchl&auml;uft Werte der mit dem Schl&uuml;sselwort <b>ParamArray <\/b>ausgestatteten Variablen <b>varParameter <\/b>in einer <b>For Next<\/b>-Schleife. Dies gelingt, indem wir als Startwert der Schleife mit <b>LBound(varParameter) <\/b>den kleinsten Index-Wert des Arrays ermitteln und als Endwert mit <b>UBound <\/b>den gr&ouml;&szlig;ten Index-Wert des Arrays angeben. Innerhalb der Schleife greift die Prozedur dann &uuml;ber <b>varParameter(i)<\/b> auf das jeweiligen Element zu.<\/p>\n<p>Hier pr&uuml;ft die Funktion zun&auml;chst den Datentyp. Im Falle des Datentyps <b>String <\/b>kann es sich um einen Nullwert oder einen echten String handeln. Ein echter String wird mit einfachen Anf&uuml;hrungszeichen eingefasst, der Wert <b>NULL <\/b>ohne Anf&uuml;hrungszeichen zur Parameterliste hinzugef&uuml;gt. Bei Dezimalzahlen wird das Komma als Dezimaltrennzeichen durch den Punkt ersetzt.<\/p>\n<p>Nach dem Zusammenf&uuml;hren aller Elemente des <b>Param-Arrays <\/b>schneidet die Funktion gegebenenfalls noch das f&uuml;hrende Komma ab und &uuml;bergibt den resultierenden Ausdruck als Funktionswert an die aufrufende Routine zur&uuml;ck.<\/p>\n<h2>Datenherkunft f&uuml;r Berichte<\/h2>\n<p>In Berichten sieht die Situation etwas anders aus als etwa in Formularen. Hier k&ouml;nnen Sie nat&uuml;rlich auch die Daten einer SQL Server-Datenbank anzeigen &#8211; aber leider nicht mithilfe der hier vorgestellten Funktionen <b>SPRecordset <\/b>und <b>SPRecordsetMitParameter<\/b>!<\/p>\n<p>Warum nicht Der Grund ist ganz einfach: Berichte unterst&uuml;tzen die Zuweisung von Recordsets zur Eigenschaft <b>Recordset <\/b>nur in ADP-Anwendungen, und die werden ja nur noch von &auml;lteren Access-Versionen unterst&uuml;tzt. Wenn Sie versuchen, einem Bericht in einer herk&ouml;mmlichen <b>.mdb<\/b>&#8211; oder <b>.accdb<\/b>-Datenbank ein Recordset zuzuweisen, erhalten Sie die Fehlermeldung aus Bild 10.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2016_06\/pic_1062_010.png\" alt=\"Die Verwendung von Recordsets als Datenquelle f&uuml;r Berichte gelingt nicht.\" width=\"649,559\" height=\"428,6685\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 10: Die Verwendung von Recordsets als Datenquelle f&uuml;r Berichte gelingt nicht.<\/span><\/b><\/p>\n<h2>Recordsource statt Recordset<\/h2>\n<p>Deshalb m&ouml;chten beziehungsweise m&uuml;ssen Sie zu verschiedenen Gelegenheiten wie etwa f&uuml;r einen Bericht vielleicht nicht die <b>Recordset<\/b>-Eigenschaft verwenden, sondern die <b>Recordsource<\/b>-Eigenschaft. Dies ist etwas schwieriger zu realisieren, da die <b>Recordsource<\/b>-Eigenschaft ja nur den Namen einer gespeicherten Tabelle oder Abfrage beziehungsweise einen SQL-Ausdruck als Wert entgegennimmt. Das ist leider nicht m&ouml;glich, ohne tats&auml;chlich eine Abfrage als eigenes Objekt in der Datenbank zu speichern.<\/p>\n<p>Die Funktion <b>SPRecordsource <\/b>erwartet genau wie die Funktion <b>SPRecordset <\/b>den Namen der gespeicherten Prozedur auf SQL Server-Seite sowie die Verbindungszeichenfolge der Datenbank (s. Listing 8). Der Name der nun zu erstellenden Pass-Through-Abfrage wird aus dem Pr&auml;fix <b>pt <\/b>und dem Namen der zu verwendenden gespeicherten Prozedur zusammengestellt, also etwa <b>ptpAlleArtikel <\/b>f&uuml;r die gespeicherte Prozedur <b>pAlleArtikel<\/b>, und in der Variablen <b>strPassThrough <\/b>gespeichert.<\/p>\n<pre>&lt;b&gt;Public Function SPRecordsource(strStoredProcedure<span style=\"color:blue;\"> As String<\/span>, strVerbindungszeichenfolge<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As String<\/span>\r\n&lt;b&gt;    Dim db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n&lt;b&gt;    Dim qdf<span style=\"color:blue;\"> As <\/span>DAO.QueryDef\r\n&lt;b&gt;    Dim strPassThrough<span style=\"color:blue;\"> As String<\/span>\r\n&lt;b&gt;    strPassThrough = \"pt\" & strStoredProcedure\r\n&lt;b&gt;    strPassThrough = <span style=\"color:blue;\">Replace<\/span>(strPassThrough, \".\", \"_\")\r\n&lt;b&gt;    <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n&lt;b&gt;    On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n&lt;b&gt;    db.QueryDefs.Delete strPassThrough\r\n&lt;b&gt;    <span style=\"color:blue;\">On Error GoTo<\/span> 0\r\n&lt;b&gt;    <span style=\"color:blue;\">Set<\/span> qdf = db.CreateQueryDef(strPassThrough)\r\n&lt;b&gt;    <span style=\"color:blue;\">With<\/span> qdf\r\n&lt;b&gt;        .Connect = strVerbindungszeichenfolge\r\n&lt;b&gt;        .SQL = \"EXEC \" & strStoredProcedure\r\n&lt;b&gt;    End <span style=\"color:blue;\">With<\/span>\r\n&lt;b&gt;    <span style=\"color:blue;\">Set<\/span> db = Nothing\r\n&lt;b&gt;    SPRecordsource = strPassThrough\r\n&lt;b&gt;End Function&lt;\/b&gt;<\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 8: Funktion zum Anlegen einer Abfrage und zum Liefern des Namens der neuen Abfrage<\/span><\/b><\/p>\n<p>Da hier gegebenenfalls der Name der gespeicherten Prozedur samt Schema &uuml;bergeben wird, also etwa <b>dbo.StoredProcedure<\/b>, Access aber keinen Punkt in Objektnamen erlaubt, m&uuml;ssen wir den Punkt durch einen Unterstrich ersetzen. Dies erledigt eine entsprechende Replace-Anweisung f&uuml;r den Inhalt der Variablen <b>strPassThrough<\/b>. Die gespeicherte Access-Abfrage hei&szlig;t dann etwa <b>spdbo_pAlleArtikel<\/b>. Sollte bereits eine Pass-Through-Abfrage gleichen Namens vorhanden sein, wird diese nun gel&ouml;scht. F&uuml;r die <b>Delete<\/b>-Methode der <b>QueryDefs<\/b>-Auflistung haben wir die eingebaute Fehlerbehandlung deaktiviert: Anderenfalls w&uuml;rde ein Fehler ausgel&ouml;st, wenn die Abfrage zuvor noch nicht angelegt wurde.<\/p>\n<p>Danach erstellt die <b>CreateQueryDef<\/b>-Methode ein neues <b>QueryDef<\/b>-Objekt mit dem soeben definierten Abfragenamen. Die Eigenschaft <b>Connect <\/b>erh&auml;lt den mit dem zweiten Parameter <b>strVerbindungszeichenfolge <\/b>&uuml;bergebenen Ausdruck und die Eigenschaft <b>SQL <\/b>die Anweisung <b>EXEC <\/b>und den Namen der mit dem ersten Parameter <b>strStoredProcedure <\/b>&uuml;bergebenen Namen der gespeicherten Abfrage. Der R&uuml;ckgabewert der Funktion enth&auml;lt dann den Namen des neu erstellten Abfrage-Objekts.<\/p>\n<p>Im Bericht selbst k&ouml;nnen Sie f&uuml;r die Ereigniseigenschaft <b>Beim &ouml;ffnen <\/b>eine Anweisung wie im folgenden Beispiel hinterlegen:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Report_Open(Cancel<span style=\"color:blue;\"> As Integer<\/span>)\r\n     Me.RecordSource = _\r\n         SPRecordsource(\"pAlleArtikel\", _\r\n         Standardverbindungszeichenfolge)\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Die neue Abfrage erscheint dann im Navigationsbereich der Datenbank-Anwendung und kann auch in der Datenblattansicht angezeigt werden (s. Bild 11). Der Bericht selbst bekommt durch die Zuweisung in der <b>Beim &ouml;ffnen<\/b>-Ereignisprozedur ebenfalls die Abfrage als Datenherkunft zugewiesen.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2016_06\/pic_1062_011.png\" alt=\"Die mit der Funktion SPRecordsource erstellte Abfrage im Navigationsbereich und in der Datenblattansicht\" width=\"649,559\" height=\"427,4081\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 11: Die mit der Funktion SPRecordsource erstellte Abfrage im Navigationsbereich und in der Datenblattansicht<\/span><\/b><\/p>\n<h2>Recordsource mit Parametern<\/h2>\n<p>Auf &auml;hnliche Weise k&ouml;nnen Sie einen Bericht auch mit den Daten f&uuml;llen, die eine gespeicherte Prozedur mit Parametern liefert (s. Bild 12).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2016_06\/pic_1062_012.png\" alt=\"Der Bericht mit der dynamisch erstellten Abfrage als Recordsource\" width=\"499,6607\" height=\"498,7179\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 12: Der Bericht mit der dynamisch erstellten Abfrage als Recordsource<\/span><\/b><\/p>\n<p>Die dazu ben&ouml;tigte Funktion namens <b>SPRecordsourceMitParameter <\/b>finden Sie in Listing 9. Der Aufbau dieser Funktion ist eine Mischung der Funktionen <b>SPRecordsetMitParameter<\/b> und <b>SPRecordsource<\/b>.<\/p>\n<pre>&lt;b&gt;Public Function SPRecordsourceMitParameter(strStoredProcedure<span style=\"color:blue;\"> As String<\/span>, strVerbindungszeichenfolge<span style=\"color:blue;\"> As String<\/span>, _\r\n&lt;b&gt;        ParamArray varParameter()<span style=\"color:blue;\"> As Variant<\/span>)<span style=\"color:blue;\"> As String<\/span>\r\n&lt;b&gt;    Dim db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n&lt;b&gt;    Dim qdf<span style=\"color:blue;\"> As <\/span>DAO.QueryDef\r\n&lt;b&gt;    Dim strParameter<span style=\"color:blue;\"> As String<\/span>\r\n&lt;b&gt;    Dim strPassThrough<span style=\"color:blue;\"> As String<\/span>\r\n&lt;b&gt;    <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n&lt;b&gt;    strPassThrough = \"pt\" & strStoredProcedure\r\n&lt;b&gt;    strPassThrough = <span style=\"color:blue;\">Replace<\/span>(strPassThrough, \".\", \"_\")\r\n&lt;b&gt;    On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n&lt;b&gt;    db.QueryDefs.Delete strPassThrough\r\n&lt;b&gt;    <span style=\"color:blue;\">On Error GoTo<\/span> 0\r\n&lt;b&gt;    <span style=\"color:blue;\">Set<\/span> qdf = db.CreateQueryDef(strPassThrough)\r\n&lt;b&gt;    strParameter = Parameterliste(varParameter)\r\n&lt;b&gt;    <span style=\"color:blue;\">With<\/span> qdf\r\n&lt;b&gt;        .Connect = strVerbindungszeichenfolge\r\n&lt;b&gt;        .SQL = \"EXEC \" & strStoredProcedure & \" \" & strParameter\r\n&lt;b&gt;    End <span style=\"color:blue;\">With<\/span>\r\n&lt;b&gt;    <span style=\"color:blue;\">Set<\/span> db = Nothing\r\n&lt;b&gt;    SPRecordsourceMitParameter = strPassThrough\r\n&lt;b&gt;End Function&lt;\/b&gt;<\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 9: Funktion zum Anlegen einer Abfrage auf Basis einer gespeicherten Prozedur mit Parametern<\/span><\/b><\/p>\n<p>Sie k&ouml;nnen diese genau wie <b>SPRecordsource <\/b>grunds&auml;tzlich auch nutzen, um auf die Schnelle eine Pass-Through-Abfrage zu erstellen. Dazu geben Sie etwa im Direktfenster einen Befehl wie den folgenden ein:<\/p>\n<pre><span style=\"color:blue;\">Debug.Print<\/span> SPRecordsourceMitParameter( \"dbo.spArtikelNachKategorieID\",  Standardverbindungszeichenfolge, 1)\r\nptdbo_spArtikelNachKategorieID<\/pre>\n<p>Die so erstellte Pass-Through-Abfrage <b>ptdbo_spArtikelNachKategorieID <\/b>finden Sie dann nat&uuml;rlich auch im Navigationsbereich der aktuellen Datenbank-Anwendung.<\/p>\n<h2>Do-m&auml;-nen-funk-tio-nen wie DLook-up nachbilden<\/h2>\n<p>Access-Entwickler verwenden gern mal Dom&auml;nenfunktionen wie <b>DLookup<\/b>, <b>DMin <\/b>oder &auml;hnliche f&uuml;r den schnellen Abruf von Daten aus lokalen Tabellen. Wenn die Tabellen der SQL Ser-ver-Datenbank in Access eingebunden sind, k&ouml;nnen Sie <b>DLookup <\/b>und Co. nat&uuml;rlich auch f&uuml;r den Zugriff auf die Daten der verkn&uuml;pften Tabellen nutzen. <b>DLookup<\/b> ist bereits in reinen Access-Anwendungen (in manchen F&auml;llen unberechtigt) verp&ouml;nt.<\/p>\n<p>Dennoch ist es eine schnelle M&ouml;g-lich-keit, mal eben aus dem Direktbereich einen Tabellenwert abzufragen. Also wollen wir ein Pendant f&uuml;r den Zugriff auf den SQL Server liefern. Die Funktion aus Listing 10 ist ein Nachbau der <b>DLookup<\/b>-Funktion und hei&szlig;t <b>SQLLookup<\/b>. Sie ermittelt die gew&uuml;nschten Daten direkt &uuml;ber eine Pass-Through-Abfrage:<\/p>\n<pre>&lt;b&gt;Public Function SQLLookup(strFeld<span style=\"color:blue;\"> As String<\/span>, strTabelle<span style=\"color:blue;\"> As String<\/span>, <span style=\"color:blue;\">Optional<\/span> strKriterium<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As Variant<\/span>\r\n&lt;b&gt;    Dim strSQL<span style=\"color:blue;\"> As String<\/span>\r\n&lt;b&gt;    Dim db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n&lt;b&gt;    Dim qdf<span style=\"color:blue;\"> As <\/span>DAO.QueryDef, rst<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n&lt;b&gt;    <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n&lt;b&gt;    <span style=\"color:blue;\">Set<\/span> qdf = db.CreateQueryDef(\"\")\r\n&lt;b&gt;    strSQL = \"SELECT \" & strTabelle & \".\" & strFeld & \" FROM \" & strTabelle\r\n&lt;b&gt;    If <span style=\"color:blue;\">Len<\/span>(strKriterium) &gt; 0 Then\r\n&lt;b&gt;        strSQL = strSQL & \" WHERE \" & strKriterium\r\n&lt;b&gt;    End If\r\n&lt;b&gt;    <span style=\"color:blue;\">With<\/span> qdf\r\n&lt;b&gt;        .Connect = Standardverbindungszeichenfolge\r\n&lt;b&gt;        .SQL = strSQL\r\n&lt;b&gt;        <span style=\"color:blue;\">Set<\/span> rst = qdf.OpenRecordset\r\n&lt;b&gt;        If <span style=\"color:blue;\">Not<\/span> rst.EOF Then\r\n&lt;b&gt;            SQLLookup = rst.Fields(0)\r\n&lt;b&gt;        Else\r\n&lt;b&gt;            SQLLookup = Null\r\n&lt;b&gt;        End If\r\n&lt;b&gt;    End <span style=\"color:blue;\">With<\/span>\r\n&lt;b&gt;End Function&lt;\/b&gt;<\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 10: Funktion zum Nachbilden der DLookup-Funktion <\/span><\/b><\/p>\n<p>Die Funktion erstellt nach dem L&ouml;schen eines eventuell vorhandenen gleichnamigen Objekts ein <b>QueryDef<\/b>-Objekt namens <b>qryLookup<\/b>. Dieses wird mit einer <b>SELECT<\/b>-Abfrage als Datenherkunft gef&uuml;llt, die mit den Parametern <b>strFeld<\/b>, <b>strTabelle <\/b>und <b>strKriterium <\/b>best&uuml;ckt wird. Die Funktion <b>Standardverbindungszeichenfolge <\/b>liefert den Verbindungsausdruck.<\/p>\n<p>Die Funktion &ouml;ffnet dann ein neues <b>Recordset<\/b>-Objekt auf Basis des <b>QueryDef<\/b>-Objekts. Be-fin-det der Datensatzzeiger sich nicht direkt hinter dem letzten Datensatz, was f&uuml;r eine leere Da-ten-satz-grup-pe sprechen w&uuml;rde, liefert <b>SQLLookup <\/b>den Wert des einzigen Feldes des Re-cord-sets zur&uuml;ck, an-de-renfalls den Wert <b>NULL<\/b>. Auf die gleiche Weise k&ouml;nnen Sie die &uuml;brigen Funk-tionen wie <b>DMin<\/b>, <b>DMax <\/b>et cetera ersetzen.<\/p>\n<p>Damit sorgen Sie zun&auml;chst daf&uuml;r, dass die Access-Anwendung nicht per <b>DLookup <\/b>auf die per ODBC eingebundenen Tabellen zugreift, sondern direkt per Pass-Through-Abfrage auf die Ta-bel-len des SQL Servers. Gegebenenfalls m&uuml;ssen Sie die Dom&auml;nenfunktionen zun&auml;chst durch solche Ersatzfunktionen ersetzen, weil die Tabellen vielleicht gar nicht per ODBC eingebunden werden sollen oder k&ouml;nnen. Ein Beispielaufruf f&uuml;r diese Funktion sieht so aus:<\/p>\n<pre>  SQLLookup(\"Artikelname\", \"tblArtikel\", \"ArtikelID = 1\")\r\nChai<\/pre>\n<p>Anderenfalls k&ouml;nnen Sie, wenn die Performance der Ersatzfunktionen nicht ausreicht, noch einen Schritt weiter gehen. Sie w&uuml;rden dann f&uuml;r jede einzelne <b>DLookup<\/b>-Funktion (und auch f&uuml;r die &uuml;brigen Dom&auml;nenfunktionen) jeweils eine eigene gespeicherte Prozedur anlegen und diese per Pass-Through-Abfrage ansprechen.<\/p>\n<p>Statt des kompletten Kriteriums w&uuml;rden Sie dann die Vergleichswerte als Parameter an die gespeicherte Prozedur &uuml;bergeben. Letztlich wurden die dazu notwendigen Techniken bereits alle weiter oben beschrieben. Davon abgesehen ist es immer sicherer, die gespeicherten Prozeduren fertig auf den SQL Server zu legen &#8211; auf diese Weise kann der Benutzer, bei entsprechenden Berechtigungen, auch nur &uuml;ber diese auf die Datenbank zugreifen.<\/p>\n<p>Dom&auml;nenfunktionen lassen sich auch als gespeicherte Prozedur auf den SQL Server &uuml;bertragen. Der zum Erstellen n&ouml;tige Code sieht so wie in Listing 11 aus.<\/p>\n<pre>&lt;b&gt;CREATE PROC [dbo].[pSqlLookup] \r\n&lt;b&gt;@Spalte sysname, \r\n&lt;b&gt;@Schema sysname = ''''dbo'''', \r\n&lt;b&gt;@Tabelle sysname, \r\n&lt;b&gt;@Bedingung nvarchar(max) = NULL\r\n&lt;b&gt;AS\r\n&lt;b&gt;DECLARE @Sql<span style=\"color:blue;\"> As <\/span>nvarchar(max);\r\n&lt;b&gt;DECLARE @Wert sql_variant;\r\n&lt;b&gt;SET @Sql = ''''SELECT '''' + @Spalte + '''' FROM '''' + @Schema + ''''.'''' + @Tabelle \r\n&lt;b&gt;IF @Bedingung Is <span style=\"color:blue;\">Not<\/span> Null\r\n&lt;b&gt;BEGIN\r\n&lt;b&gt;       SET @Sql = @Sql + '''' WHERE '''' + @Bedingung\r\n&lt;b&gt;END\r\n&lt;b&gt;EXEC sp_executesql @sql;&lt;\/b&gt;<\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 11: Erstellen eines Pendants zur DLookup-Funktion als gespeicherte Prozedur<\/span><\/b><\/p>\n<p>Die gespeicherte Prozedur setzt aus den &uuml;bergebenen Parametern einen SQL-Ausdruck zusam-men und speichert diesen in der Variablen <b>@Sql<\/b>. Deren Inhalt wird an die eingebaute gespeicherte Prozedur <b>sp_executesql <\/b>&uuml;bergeben, die dann den SQL-Ausdruck ausf&uuml;hrt und das Er-geb-nis zur&uuml;ckliefert.<\/p>\n<p>Der Aufruf sieht im Direktfenster etwa so aus:<\/p>\n<pre>  SPRecordsetMitParameter(\"pSQLLookup\", standardverbindungszeichenfolge, \"Artikelname\", \"dbo\", \"tblArtikel\", \"ArtikelID = 1\").Fields(0)\r\nChai<\/pre>\n<p>Wir k&ouml;nnen den Aufruf der gespeicherten Prozedur auch in einer VBA-Funktion kapseln, welche die Parameter der Funktion <b>SPRecordsetMitParameter <\/b>&uuml;bergibt (siehe weiter oben) und das Ergebnisfeld des zur&uuml;ckgelieferten Recordsets ausliest (s. Listing 12).<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>SPLookup(strFeld<span style=\"color:blue;\"> As String<\/span>, strTabelle<span style=\"color:blue;\"> As String<\/span>, <span style=\"color:blue;\">Optional<\/span> strKriterium<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As Variant<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>rst<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> <span style=\"color:blue;\">Len<\/span>(strKriterium) = 0<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">Set<\/span> rst = SPRecordsetMitParameter(\"pSQLLookup\", _\r\n             Standardverbindungszeichenfolge, strFeld, \"dbo\", strTabelle, strKriterium)\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         <span style=\"color:blue;\">Set<\/span> rst = SPRecordsetMitParameter(\"pSQLLookup\", _\r\n             Standardverbindungszeichenfolge, strFeld, \"dbo\", strTabelle)\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     SPLookup = rst.Fields(0)\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 12: Funktion f&uuml;r den direkten Zugriff auf die gespeicherte Prozedur f&uuml;r den Tabellen-Lookup<\/span><\/b><\/p>\n<h2>Zusammenfassung und Ausblick<\/h2>\n<p>Mit den hier vorgestellten Techniken greifen Sie auf die Daten der Tabellen einer SQL Server-Datenbank zu. Bei den ersten Beispielen haben wir dies noch &uuml;ber ein Recordset auf eine eingebundene Tabelle erledigt, danach haben wir aber die auf dem Server befindlichen gespeicherten Prozeduren genutzt.<\/p>\n<p>Dazu haben wir vier praktische Funktionen programmiert, mit denen Sie jeweils ein Recordset mit und ohne Parameter und eine als Recordsource nutzbare Abfrage, auch mit und ohne Parameter, erstellen k&ouml;nnen.<\/p>\n<p>Schlie&szlig;lich haben wir noch eine Funktion programmiert, mit der Sie &auml;hnlich wie mit der <b>DLookup<\/b>-Funktion von Access auf einen Wert eines bestimmten Datensatzes zugreifen k&ouml;nnen. Dabei haben wir die ben&ouml;tigte Abfrage noch dynamisch zusammengestellt und als SQL-Ausdruck zum SQL Server geschickt, der den Rest der Arbeit &uuml;bernommen hat.<\/p>\n<p>Eine noch fortgeschrittenere Version haben wir erstellt, indem wir die DLookup-Funktion auf dem SQL Server in Form einer gespeicherten Prozedur programmiert haben, die Sie &uuml;ber die zuvor vorgestellten Funktionen ansprechen k&ouml;nnen.<\/p>\n<h3>Downloads zu diesem Beitrag<\/h3>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>RDBMSPerVBA_.accdb<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/ADF82E44-161F-48F4-BCFF-9A98CBE900F9\/aiu_1062.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. Nun gehen wir einen Schritt weiter: Wir wollen mit den dort beschriebenen Methoden etwa zum Zusammenstellen einer Verbindungszeichenfolge auf die Daten einer SQL Server-Datenbank zugreifen. Dabei lernen Sie eine Reihe interessanter Funktionen kennen, die den Zugriff deutlich vereinfachen und die auch noch &uuml;beraus performant sind.<\/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":[662016,66062016,44000022],"tags":[],"class_list":["post-55001062","post","type-post","status-publish","format-standard","hentry","category-662016","category-66062016","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 abfragen - 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_abfragen\/\" \/>\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 abfragen\" \/>\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. Nun gehen wir einen Schritt weiter: Wir wollen mit den dort beschriebenen Methoden etwa zum Zusammenstellen einer Verbindungszeichenfolge auf die Daten einer SQL Server-Datenbank zugreifen. Dabei lernen Sie eine Reihe interessanter Funktionen kennen, die den Zugriff deutlich vereinfachen und die auch noch &uuml;beraus performant sind.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_abfragen\/\" \/>\n<meta property=\"og:site_name\" content=\"Access im Unternehmen\" \/>\n<meta property=\"article:published_time\" content=\"2020-05-22T13:34:44+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/vg09.met.vgwort.de\/na\/4ea130ebb15e4690b50c0d5ddbe5603e\" \/>\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=\"30\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_abfragen\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/RDBMSZugriff_per_VBA_Daten_abfragen\\\/\"},\"author\":{\"name\":\"Andr\u00e9 Minhorst\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\"},\"headline\":\"RDBMS-Zugriff per VBA: Daten abfragen\",\"datePublished\":\"2020-05-22T13:34:44+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/RDBMSZugriff_per_VBA_Daten_abfragen\\\/\"},\"wordCount\":4839,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/RDBMSZugriff_per_VBA_Daten_abfragen\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg09.met.vgwort.de\\\/na\\\/4ea130ebb15e4690b50c0d5ddbe5603e\",\"articleSection\":[\"2016\",\"6\\\/2016\",\"SQL Server und Co.\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/RDBMSZugriff_per_VBA_Daten_abfragen\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/RDBMSZugriff_per_VBA_Daten_abfragen\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/RDBMSZugriff_per_VBA_Daten_abfragen\\\/\",\"name\":\"RDBMS-Zugriff per VBA: Daten abfragen - Access im Unternehmen\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/RDBMSZugriff_per_VBA_Daten_abfragen\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/RDBMSZugriff_per_VBA_Daten_abfragen\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg09.met.vgwort.de\\\/na\\\/4ea130ebb15e4690b50c0d5ddbe5603e\",\"datePublished\":\"2020-05-22T13:34:44+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/RDBMSZugriff_per_VBA_Daten_abfragen\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/RDBMSZugriff_per_VBA_Daten_abfragen\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/RDBMSZugriff_per_VBA_Daten_abfragen\\\/#primaryimage\",\"url\":\"http:\\\/\\\/vg09.met.vgwort.de\\\/na\\\/4ea130ebb15e4690b50c0d5ddbe5603e\",\"contentUrl\":\"http:\\\/\\\/vg09.met.vgwort.de\\\/na\\\/4ea130ebb15e4690b50c0d5ddbe5603e\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/RDBMSZugriff_per_VBA_Daten_abfragen\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"RDBMS-Zugriff per VBA: Daten abfragen\"}]},{\"@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 abfragen - 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_abfragen\/","og_locale":"de_DE","og_type":"article","og_title":"RDBMS-Zugriff per VBA: Daten abfragen","og_description":"Im Beitrag \"RDBMS-Zugriff per VBA: Verbindungen\" haben wir die Grundlage f&uuml;r den Zugriff auf SQL Server-Datenbanken geschaffen. Nun gehen wir einen Schritt weiter: Wir wollen mit den dort beschriebenen Methoden etwa zum Zusammenstellen einer Verbindungszeichenfolge auf die Daten einer SQL Server-Datenbank zugreifen. Dabei lernen Sie eine Reihe interessanter Funktionen kennen, die den Zugriff deutlich vereinfachen und die auch noch &uuml;beraus performant sind.","og_url":"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_abfragen\/","og_site_name":"Access im Unternehmen","article_published_time":"2020-05-22T13:34:44+00:00","og_image":[{"url":"http:\/\/vg09.met.vgwort.de\/na\/4ea130ebb15e4690b50c0d5ddbe5603e","type":"","width":"","height":""}],"author":"Andr\u00e9 Minhorst","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Andr\u00e9 Minhorst","Gesch\u00e4tzte Lesezeit":"30\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_abfragen\/#article","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_abfragen\/"},"author":{"name":"Andr\u00e9 Minhorst","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f"},"headline":"RDBMS-Zugriff per VBA: Daten abfragen","datePublished":"2020-05-22T13:34:44+00:00","mainEntityOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_abfragen\/"},"wordCount":4839,"commentCount":0,"publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_abfragen\/#primaryimage"},"thumbnailUrl":"http:\/\/vg09.met.vgwort.de\/na\/4ea130ebb15e4690b50c0d5ddbe5603e","articleSection":["2016","6\/2016","SQL Server und Co."],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_abfragen\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_abfragen\/","url":"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_abfragen\/","name":"RDBMS-Zugriff per VBA: Daten abfragen - Access im Unternehmen","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_abfragen\/#primaryimage"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_abfragen\/#primaryimage"},"thumbnailUrl":"http:\/\/vg09.met.vgwort.de\/na\/4ea130ebb15e4690b50c0d5ddbe5603e","datePublished":"2020-05-22T13:34:44+00:00","breadcrumb":{"@id":"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_abfragen\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_abfragen\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_abfragen\/#primaryimage","url":"http:\/\/vg09.met.vgwort.de\/na\/4ea130ebb15e4690b50c0d5ddbe5603e","contentUrl":"http:\/\/vg09.met.vgwort.de\/na\/4ea130ebb15e4690b50c0d5ddbe5603e"},{"@type":"BreadcrumbList","@id":"https:\/\/access-im-unternehmen.de\/RDBMSZugriff_per_VBA_Daten_abfragen\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/access-im-unternehmen.de\/"},{"@type":"ListItem","position":2,"name":"RDBMS-Zugriff per VBA: Daten abfragen"}]},{"@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\/55001062","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=55001062"}],"version-history":[{"count":0,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55001062\/revisions"}],"wp:attachment":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/media?parent=55001062"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/categories?post=55001062"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/tags?post=55001062"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}