{"id":55001491,"date":"2024-04-01T00:00:00","date_gmt":"2024-02-29T19:02:54","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=1491"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"SQL_Server_Von_der_Abfrage_zur_Stored_Procedure","status":"publish","type":"post","link":"https:\/\/access-im-unternehmen.de\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\/","title":{"rendered":"SQL Server: Von der Abfrage zur Stored Procedure"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg02.met.vgwort.de\/na\/5f008ae4b2a940bcb34e4024370481cf\" width=\"1\" height=\"1\" alt=\"\"><\/p>\n<p><b>Die einfache Migration der Tabellen einer Datenbank von Access zum SQL Server ist meist schnell erledigt. Der SQL Server Migration Assistant (SSMA) leistet gute Arbeit und schnell landen alle Tabellen in der SQL Server-Datenbank &#8211; samt Erstellung entsprechender Tabellenverkn&uuml;pfungen im Access-Frontend. Damit l&auml;sst sich erst einmal arbeiten, da die Verkn&uuml;pfungen Lesen und Schreiben der Daten wie gewohnt zulassen. Fr&uuml;her oder sp&auml;ter wird man jedoch auf Abfragen im Access-Frontend sto&szlig;en, die schlicht zu langsam sind. Und hier kann der SQL Server sein wahres Potential ausspielen: Zum Beispiel, indem wir dort eine Stored Procedure (Gespeicherte Abfrage) anlegen, welche die Abfrage f&uuml;r uns direkt auf dem SQL Server ausf&uuml;hrt, was viel schneller gehen wird. Und genau das ist Thema dieses Beitrags: Wie bekommen wir eine mehr oder weniger komplizierte Abfrage als Stored Procedure zum SQL Server und rufen diese von der Access-Datenbank aus auf?<\/b><\/p>\n<p>Der SQL Server hat gegen&uuml;ber einer Access-Datenbank eine besondere St&auml;rke: Im Vergleich zu der Fileserver-Datenbank von Access kann der SQL Server Abfragen direkt auf dem Server ausf&uuml;hren und nur die Daten ausliefern, die tats&auml;chlich ben&ouml;tigt werden.<\/p>\n<p>Bei Verwendung einer aufgeteilten Access-Datenbank werden immer alle Daten auf den anfragenden Rechner &uuml;bertragen und dort ausgewertet. Was schneller geht, liegt auf der Hand, vor allem, je mehr Daten von der Abfrage betroffen sind.<\/p>\n<p>Deshalb schauen wir uns in diesem Beitrag im Detail an, wie wir eine reine Access-Abfrage zum SQL Server transferieren und dort eine Stored Procedure daraus erzeugen. <\/p>\n<h2>Voraussetzungen<\/h2>\n<p>Damit wir in diesem Artikel reibungslos starten k&ouml;nnen, setzen wir voraus, dass die grundlegenden Migrationsarbeiten schon erledigt sind &#8211; also dass die Tabellen der Datenbank bereits auf dem SQL Server liegen und vom Frontend aus per ODBC-Tabellenverkn&uuml;pfung gelesen und geschrieben werden k&ouml;nnen.<\/p>\n<h2>Vor- und Nachteile von Stored Procedures<\/h2>\n<p>Kurz die wichtigsten Gr&uuml;nde f&uuml;r oder gegen gespeicherte Prozeduren: Gespeicherte Prozeduren k&ouml;nnen genau wie Access-Abfragen mit Parametern und allen anderen dort genutzten Funktionen verwendet werden und sie sind sehr schnell. Der Preis, den man bei Verwendung gespeicherter Prozeduren bezahlt, ist die fehlende M&ouml;glichkeit zum &Auml;ndern der Daten. Wenn wir eine gespeicherte Prozedur zum Lesen von Daten nutzen, um eine Access-Abfrage zu ersetzen, die auf Basis von per ODBC verkn&uuml;pften Tabellen funktioniert, k&ouml;nnen wir die gelieferten Daten nicht direkt &auml;ndern beziehungsweise neue Datens&auml;tze anlegen oder vorhandene Datens&auml;tze l&ouml;schen.<\/p>\n<p>Das ist aber in vielen F&auml;llen auch nicht n&ouml;tig &#8211; wir k&ouml;nnen mit gespeicherten Prozeduren erst einmal Daten etwa f&uuml;r &Uuml;bersichten mit allen Datens&auml;tzen einer Tabelle holen, diese in einem Formular anzeigen und in einem weiteren Formular den zu bearbeitenden Datensatz erneut einlesen &#8211; diesmal auf Basis der per ODBC verkn&uuml;pften Tabelle.<\/p>\n<h2>Beispielabfrage aus der Praxis<\/h2>\n<p>Anlass f&uuml;r diesen Beitrag war eine Abfrage, mit der ich die Kunden ermittle, die in einem bestimmten Zeitraum ein oder mehrere bestimmte Produkte erworben haben und f&uuml;r die ich noch weitere Kriterien festlegen wollte.<\/p>\n<p>Die Abfrage habe ich bisher in Access auf die ODBC-Tabellenverkn&uuml;pfungen angewendet. Die Abfrage lautet beispielsweise:<\/p>\n<pre>SELECT DISTINCT KundeID, AnredeID, Nachname, Vorname, EMail, Land, Land_Rechnung FROM tblKunden WHERE KundeID IN (    SELECT tblAbonnements.KundeID     FROM tblAbonnements     WHERE tblAbonnements.ProduktID IN (146)     AND Startdatum &gt;= #2024\/01\/01 00:00:00#     AND Startdatum &lt;= #2024\/02\/01 00:00:00#) \r\nAND KundeID NOT IN (\r\n     SELECT tblAbonnements.KundeID \r\n     FROM tblAbonnements \r\n     WHERE tblAbonnements.ProduktID IN (96, 97)AND tblKunden.NLAbgemeldetAm IS NULL AND (    tblKunden.Land = ''Deutschland''     OR tblKunden.Land_Rechnung = ''Deutschland'')<\/pre>\n<p>Ich ben&ouml;tige also alle Kunden, denen &uuml;ber die Tabelle <b>tblAbonnements <\/b>das Abonnement mit der Nummer <b>146 <\/b>zugeteilt wurde und deren Startdatum in einem bestimmten Datumsbereich liegt.<\/p>\n<p>Zus&auml;tzlich soll der Kunde nicht die Produkte <b>96 <\/b>und <b>97 <\/b>gebucht haben. Au&szlig;erdem sollte der Kunde sich noch nicht vom Newsletter abgemeldet haben und entweder die Liefer- oder die Rechnungsadresse sollte in Deutschland liegen.<\/p>\n<p>Diese Abfrage wird zur Laufzeit per VBA zusammengestellt. Die notwendigen Parameter ermittle ich mit dem Formular aus Bild 1. Hier lasse ich mir zuerst alle Produkte anzeigen, die einen bestimmten Namen haben, &uuml;bertrage dieses Produkt in die Liste oben rechts und f&uuml;ge gegebenenfalls noch Produkte in die Liste der nicht bestellten Produkte hinzu. Darunter gebe ich das Bestelldatum ein und das Land des Kunden. Au&szlig;erdem kann ich noch angeben, ob Kunden ausgeschlossen werden sollen, die den Newsletter abgemeldet haben.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_02\/pic_1491_001.png\" alt=\"Formular zum Zusammenstellen der Abfrage\" width=\"649,559\" height=\"397,2883\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Formular zum Zusammenstellen der Abfrage<\/span><\/b><\/p>\n<p>Diese Parameter unter VBA zu einer funktionierenden Access-Abfrage zusammenzustellen ist eine Flei&szlig;arbeit, aber f&uuml;r jeden Programmierer zu bewerkstelligen. Der Code enth&auml;lt eine Reihe von <b>If&#8230;Then<\/b>-Bedingungen, die pr&uuml;fen, ob eine bestimmte Option einen Wert enth&auml;lt und f&uuml;gt die entsprechenden Kriterien zur Abfrage hinzu.<\/p>\n<h2>Der Weg zur Stored Procedure<\/h2>\n<p>Um diese Abfrage in eine gespeicherte Prozedur umzuwandeln und diese zu nutzen, sind einige Schritte n&ouml;tig:<\/p>\n<ul>\n<li>&Uuml;bertragen einer Version der Abfrage mit m&ouml;glichst allen Kriterien in die Zwischenablage<\/li>\n<li>Einf&uuml;gen des SQL-Codes in eine Stored Procedure<\/li>\n<li>Beheben der ersten auftretenden Syntaxfehler<\/li>\n<li>Ausprobieren, ob die Performance tats&auml;chlich besser ist als in der Access-Abfrage<\/li>\n<li>Schrittweises Einf&uuml;hren von Parametern und Aufruf mit oder ohne die jeweiligen Parameter<\/li>\n<li>Wenn alle Parameter abgebildet sind, den Aufruf der gespeicherten Prozedur in der Access-Datenbank realisieren.<\/li>\n<\/ul>\n<h2>Anlegen der Stored Procedure<\/h2>\n<p>Die gespeicherte Prozedur legen wir im SQL Server Management Studio &uuml;ber den Kontextmen&uuml;-Befehl <b>Neu|Gespeicherte Prozedur&#8230; <\/b>des Eintrags <b>Programmierbarkeit|Gespeicherte Prozeduren <\/b>der jeweiligen Datenbank (siehe Bild 2).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_02\/pic_1491_002.png\" alt=\"Hinzuf&uuml;gen einer neuen gespeicherten Prozedur\" width=\"649,559\" height=\"471,2661\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Hinzuf&uuml;gen einer neuen gespeicherten Prozedur<\/span><\/b><\/p>\n<p>Damit erhalten wir eine Vorlage f&uuml;r eine neue gespeicherte Prozedur (siehe Bild 3, hier mit bereits entfernten Kommentaren). Was wir hier sehen, ist &uuml;brigens nicht die gespeicherte Prozedur selbst, sondern der Code, mit dem wir diese anlegen.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_02\/pic_1491_003.png\" alt=\"Vorlage f&uuml;r eine neue gespeicherte Prozedur\" width=\"649,559\" height=\"314,3611\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 3: Vorlage f&uuml;r eine neue gespeicherte Prozedur<\/span><\/b><\/p>\n<p>Die gespeicherte Prozedur selbst bekommen wir nie zu sehen &#8211; wir k&ouml;nnen uns sp&auml;ter nur den Code ausgeben lassen, mit dem wir die gespeicherte Prozedur erneut anlegen oder &auml;ndern k&ouml;nnen.<\/p>\n<p>Haben wir den Code zum Erstellen der gespeicherten Prozedur einmal ausgef&uuml;hrt und wollen diese nun &auml;ndern, m&uuml;ssen wir den Befehl <b>CREATE PROCEDURE <\/b>durch <b>ALTER PROCEDURE <\/b>&auml;ndern.<\/p>\n<p>Doch eins nach dem anderen. Erst einmal &auml;ndern wir den Namen der gespeicherten Prozedur, indem wir <b><Procedure_Name, sysname, ProcedureName> <\/b>durch unseren Namen ersetzen &#8211; zum Beispiel <b>dbo.spGetCustomersWithSpecificProducts<\/b>.<\/p>\n<p>Die Parameter l&ouml;schen wir zuerst einmal, dann f&uuml;gen wir statt der vorhandenen <b>SELECT<\/b>-Anweisung unsere Abfrage aus der Access-Datenbank ein. Hier erhalten wir gleich markierte Stellen mit Fehlern (siehe Bild 4).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_02\/pic_1491_004.png\" alt=\"Fehler im ersten Entwurf\" width=\"599,559\" height=\"403,373\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 4: Fehler im ersten Entwurf<\/span><\/b><\/p>\n<p>Ich habe mich erst um den zweiten Fehler k&uuml;mmern wollen, stellte aber dann fest, dass es sich nur um einen Folgefehler des ersten Fehlers handelte. Es empfiehlt sich also, Fehler in T-SQL von oben nach unten zu korrigieren, um solche Probleme zu verhindern.<\/p>\n<p>Das Datum weist offensichtlich ein ung&uuml;ltiges Format auf. Wenn wir es in <b>&#8220;2024-01-01&#8220; <\/b>und <b>&#8220;2024-02-01&#8220; <\/b>&auml;ndern, verschwinden alle Fehler.<\/p>\n<h2>Erstmaliges Erstellen der gespeicherten Prozedur<\/h2>\n<p>Nun erstellen wir die Prozedur durch Bet&auml;tigen der Taste <b>F5 <\/b>oder mit dem Men&uuml;befehl <b>Abfrage|Ausf&uuml;hren<\/b>.<\/p>\n<p>Dies liefert die Meldung, dass die Befehle erfolgreich ausgef&uuml;hrt wurden und nach einer Aktualisierung des Punktes <b>Gespeicherte Prozeduren <\/b>im Objekt-Explorer finden wir die neue gespeicherte Prozedur dort vor.<\/p>\n<h2>Ausf&uuml;hren der gespeicherten Prozedur<\/h2>\n<p>Um die gespeicherte Prozedur auszuf&uuml;hren, k&ouml;nnen wir ein neues Abfragefenster &ouml;ffnen oder wir geben den Aufruf der Abfrage einfach in das bestehende Abfragefenster ein, in dem wir die gespeicherte Prozedur erstellt haben. Der Aufruf enth&auml;lt den Befehl <b>EXEC <\/b>und den Namen der auszuf&uuml;hrenden gespeicherten Prozedur:<\/p>\n<pre>EXEC spGetCustomersWithSpecificProducts<\/pre>\n<p>Wenn wir diesen Aufruf im gleichen Abfragefenster wir die <b>CREATE PROCEDURE <\/b>anlegen, m&uuml;ssen wir diesen markieren und dann die Taste <b>F5 <\/b>bet&auml;tigen. Wenn wir zuvor nicht markieren, werden alle Befehle im Abfragefenster ausgef&uuml;hrt, also auch die <b>CREATE<\/b>-Methode. Dies f&uuml;hrt zu einem Fehler, weil wir die gespeicherte Prozedur bereits erstellt haben.<\/p>\n<p>F&uuml;hren wir die gespeicherte Prozedur jedoch separat aus, erhalten wir die gew&uuml;nschten Datens&auml;tze (siehe Bild 5).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_02\/pic_1491_005.png\" alt=\"Abfrageergebnis\" width=\"599,559\" height=\"301,6242\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 5: Abfrageergebnis<\/span><\/b><\/p>\n<h2>Anpassen der gespeicherten Prozedur<\/h2>\n<p>Wenn die die gespeicherte Prozedur nun anpassen wollen, k&ouml;nnen wir direkt in dem Abfragefenster bleiben, in dem wir auch den <b>CREATE PROCEDURE<\/b>-Befehl aufgerufen haben. Diesen ersetzen wir einfach durch den <b>ALTER PROCEDURE<\/b>-Befehl. Damit &auml;ndern wir die bestehende gespeicherte Prozedur wie in der Anweisung angegeben.<\/p>\n<p>Wenn wir nach dem &Auml;ndern immer gleich testen wollen, wie das aktuelle Ergebnis aussieht, k&ouml;nnen wir die beiden Anweisungen auch direkt untereinander platzieren. Wir erstellen dann zuerst die neue Version der Abfrage und f&uuml;hren diese dann direkt aus.<\/p>\n<h2>Tipp: IntelliSense-Cache aktualisieren<\/h2>\n<p>Manchmal markiert der SQL Server den Namen einer bereits angelegten gespeicherten Prozedur oder von anderen Elementen. Dann ist m&ouml;glicherweise der IntelliSense-Cache noch nicht aktualisiert. Das k&ouml;nnen wir auf zwei Arten nachholen:<\/p>\n<ul>\n<li>Tastenkombination <b>Strg + Umschalt + R <\/b>dr&uuml;cken<\/li>\n<li>Men&uuml;befehl <b>Bearbeiten|IntelliSense|Lokalen Cache aktualisieren <\/b>bet&auml;tigen<\/li>\n<\/ul>\n<h2>Abfrage von Access aus ausf&uuml;hren<\/h2>\n<p>Um die Abfrage in der jetzigen Form auszuf&uuml;hren, also ohne Parameter, f&uuml;hren wir die folgenden Schritte aus:<\/p>\n<ul>\n<li>Erstellen einer neuen Abfrage in der Entwurfsansicht<\/li>\n<li>Bet&auml;tigen des Ribbonbefehls <b>Abfrageentwurf|Abfragetyp|Pass-Through<\/b><\/li>\n<li>Einf&uuml;gen des Aufrufs der Abfrage, also <b>EXEC dbo.spGetCustomersWithSpecificProducts<\/b><\/li>\n<li>Eintragen der Verbindungszeichenfolge f&uuml;r die Eigenschaft ODBC-Verbindung.<\/li>\n<\/ul>\n<p>Die Verbindungszeichenfolge kann in der Regel einer der per ODBC eingebundenen Tabellen entnommen werden &#8211; man braucht nur hinten den Parameter <b>TABLE <\/b>zu entfernen (siehe Bild 6). In unserem Fall lautet die Verbindungszeichenfolge mit dem allgemeinen SQL Server-Treiber so:<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_02\/pic_1491_006.png\" alt=\"Abfrageentwurf f&uuml;r den Zugriff auf eine gespeicherte Prozedur\" width=\"700\" height=\"304,243\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 6: Abfrageentwurf f&uuml;r den Zugriff auf eine gespeicherte Prozedur<\/span><\/b><\/p>\n<pre>ODBC;DRIVER=SQL Server;SERVER=amvdesktop2019;Trusted_Connection=Yes;DATABASE=Kundenverwaltung<\/pre>\n<p>Wechseln wir nun in die Datenblattansicht der Abfrage, finden wir die gew&uuml;nschten Datens&auml;tze in Millisekunden wie in Bild 7 vor.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_02\/pic_1491_007.png\" alt=\"Ergebnis in Access in der Datenblattansicht der Abfrage\" width=\"649,559\" height=\"213,84\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 7: Ergebnis in Access in der Datenblattansicht der Abfrage<\/span><\/b><\/p>\n<h2>Parameter hinzuf&uuml;gen<\/h2>\n<p>Damit kommen wir zum n&auml;chsten Schritt. Bisher liefert die Abfrage die Daten f&uuml;r die Parameter beziehungsweise Kriterien, die wir der Beispielabfrage entnommen haben. Wir wollen die Parameter aber von Access aus ermitteln und an die gespeicherte Prozedur &uuml;bermitteln, damit diese auf dem SQL Server ausgewertet werden k&ouml;nnen.<\/p>\n<p>Dazu ersetzen wir zuerst die Vergleichswerte durch entsprechende Parameter. Dabei k&ouml;nnen wir uns Schritt f&uuml;r Schritt durch die Abfrage arbeiten. Als Erstes wollen wir die Produkt-ID in dieser Zeile durch einen Parameter ersetzen:<\/p>\n<pre>WHERE tblAbonnements.ProduktID IN (146)<\/pre>\n<p>Hier stellt sich gleich die Frage, ob wir einen Zahlenwert oder eine Zeichenkette &uuml;bergeben. Immerhin ermitteln wir die zu untersuchenden Produkt-IDs in der aufrufenden Access-Anwendung und f&uuml;gen diese zu einer Komma-separierten Liste zusammen &#8211; statt <b>146 <\/b>k&ouml;nnte hier also auch <b>146, 147, 148 <\/b>stehen. Der Einfachheit halber wollen wir hier mit einer Zeichenkette arbeiten. Dazu f&uuml;gen wir der Abfrage einen Parameter namens <b>@Produkte <\/b>mit dem Datentyp <b>VARCHAR(255) <\/b>hinzu. Au&szlig;erdem f&uuml;gen wir diesen Parameter in die Klammern hinter dem <b>IN<\/b>-Schl&uuml;sselwort ein:<\/p>\n<pre>ALTER PROCEDURE spGetCustomersWithSpecificProducts(\r\n   @Produkte VARCHAR(255)\r\n)\r\nAS\r\nBEGIN\r\n&nbsp;&nbsp;&nbsp;&nbsp;SET NOCOUNT ON;\r\n&nbsp;&nbsp;&nbsp;&nbsp;SELECT DISTINCT KundeID, AnredeID, Nachname,            Vorname, EMail, Land, Land_Rechnung \r\n&nbsp;&nbsp;&nbsp;&nbsp;FROM tblKunden \r\n&nbsp;&nbsp;&nbsp;&nbsp;WHERE KundeID IN (\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SELECT tblAbonnements.KundeID \r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FROM tblAbonnements \r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WHERE tblAbonnements.ProduktID                  IN (@Produkte)<\/pre>\n<p>Dies funktioniert nun mit folgendem Aufruf:<\/p>\n<pre>EXEC dbo.spGetCustomersWithSpecificProducts \"146\"<\/pre>\n<p>Dann versuchen wir es mit dem folgenden Wert f&uuml;r den Parameter:<\/p>\n<pre>EXEC dbo.spGetCustomersWithSpecificProducts \"146, 147\"<\/pre>\n<p>Das funktioniert nicht und wir erhalten die folgende Fehlermeldung:<\/p>\n<pre>Nachricht 245, Stufe 16, Status 1, Prozedur dbo.spGetCustomersWithSpecificProducts, Zeile 7 [Batchstartzeile 24]\r\nFehler beim Konvertieren des varchar-Werts \"146, 147\" in den int-Datentyp.<\/pre>\n<p>Der SQL Server versucht, die Zeichenfolge <b>146, 147 <\/b>in den Datentyp des Feldes umzuwandeln, was hier nicht funktioniert. Wir m&uuml;ssen also schon an dieser Stelle zu einer Technik greifen, die sich dynamisches SQL nennt. Das ist nichts anderes, als wenn wir einen SQL-Ausdruck in VBA zusammensetzen &#8211; nur, dass dies im SQL Server geschieht. F&uuml;r unseren Anwendungsfall haben wir nun die &Auml;nderungen wie in Listing 1 vorgenommen. Hier sehen wir, dass wir eine Variable namens <b>@SQL <\/b>mit dem Datentyp <b>NVARCHAR(MAX) <\/b>angelegt haben. Diese nimmt unseren SQL-Ausdruck auf, wobei wir hier den Inhalt von <b>@Produkte <\/b>in die Zeichenkette integrieren. Am Ende rufen wir die eingebaute gespeicherte Prozedur <b>sp_executesql <\/b>auf und &uuml;bergeben dieser die SQL-Abfrage aus <b>@SQL<\/b>.<\/p>\n<pre>ALTER PROCEDURE spGetCustomersWithSpecificProducts(\r\n   @Produkte VARCHAR(255)\r\n)\r\nAS\r\nBEGIN\r\n&nbsp;&nbsp;&nbsp;&nbsp;SET NOCOUNT ON;\r\n&nbsp;&nbsp;&nbsp;&nbsp;DECLARE @SQL NVARCHAR(MAX);\r\n&nbsp;&nbsp;&nbsp;&nbsp;SET @SQL = N''SELECT DISTINCT KundeID, AnredeID, Nachname, Vorname, EMail, Land, Land_Rechnung \r\n&nbsp;&nbsp;&nbsp;&nbsp;FROM tblKunden \r\n&nbsp;&nbsp;&nbsp;&nbsp;WHERE KundeID IN (\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SELECT tblAbonnements.KundeID \r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FROM tblAbonnements \r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WHERE tblAbonnements.ProduktID IN ('' + @Produkte + '') \r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AND Startdatum &gt;= ''''2024-01-01'''' \r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AND Startdatum &lt;= ''''2024-02-01''''\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;) \r\n&nbsp;&nbsp;&nbsp;&nbsp;AND KundeID NOT IN (\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SELECT tblAbonnements.KundeID \r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FROM tblAbonnements \r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WHERE tblAbonnements.ProduktID IN (96, 97)\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;)\r\n&nbsp;&nbsp;&nbsp;&nbsp;AND (Land = ''''Deutschland'''' \r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OR Land_Rechnung = ''''Deutschland'''')''\r\n&nbsp;&nbsp;&nbsp;&nbsp;EXEC sp_executesql @SQL;\r\nEND\r\nGO\r\nEXEC dbo.spGetCustomersWithSpecificProducts \"146, 147\"<\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Gespeicherte Prozedur mit dynamischem SQL<\/span><\/b><\/p>\n<p>Das Ergebnis ist das gleiche wie zuvor &#8211; nur dass wir jetzt auch Ausdr&uuml;cke wie <b>146, 147, 148 <\/b>f&uuml;r den Parameter <b>@Kunde <\/b>&uuml;bergeben k&ouml;nnen.<\/p>\n<p>Auf die gleiche Weise f&uuml;gen wir nun den Parameter hinzu, mit dem wir Kunden ausschlie&szlig;en wollen, die ein oder mehrere andere Produkte gekauft haben:<\/p>\n<pre>ALTER PROCEDURE spGetCustomersWithSpecificProducts(\r\n   @Produkte VARCHAR(255),\r\n   @NichtProdukte VARCHAR(255)\r\n)\r\n...\r\n     WHERE tblAbonnements.ProduktID IN ('' + @NichtProdukte + '')\r\n...<\/pre>\n<p>Der Aufruf lautet nun beispielsweise wie folgt und liefert alle Kunden, welche die Produkte 146 oder 147 gekauft haben, aber nicht 1, 2, 3, 4, 5 oder 6:<\/p>\n<pre>EXEC dbo.spGetCustomersWithSpecificProducts \"146, 147\", \"1, 2, 3, 4, 5, 6\"<\/pre>\n<h2>Datum und Land als Parameter<\/h2>\n<p>Wenn wir wie hier mit dynamischem SQL arbeiten, k&ouml;nnen wir auch das Datum und das Land direkt als Zeichenkette &uuml;bergeben. Dazu definieren wir die entsprechenden Variablen und f&uuml;gen diese analog zu den vorherigen Parametern in die Abfrage ein:<\/p>\n<pre>ALTER PROCEDURE spGetCustomersWithSpecificProducts(\r\n   ...\r\n   @DatumVon VARCHAR(50),\r\n   @DatumBis VARCHAR(50),\r\n   @Land VARCHAR(50)\r\n)\r\n...\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AND Startdatum &gt;= '''''' + @DatumVon + '''''' \r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AND Startdatum &lt;= '''''' + @DatumBis + ''''''\r\n...\r\n&nbsp;&nbsp;&nbsp;&nbsp;AND (Land = '''''' + @Land + '''''' \r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OR Land_Rechnung = '''''' + @Land + '''''')''<\/pre>\n<p>Der Aufruf lautet dann:<\/p>\n<pre>EXEC dbo.spGetCustomersWithSpecificProducts \"146, 147\", \"1, 2, 3, 4, 5, 6\", \"2024-01-01\", \"2024-02-01\", \"Deutschland\"<\/pre>\n<h2>Parameter weglassen<\/h2>\n<p>Nun wollen wir nicht immer alle Parameter nutzen. Wenn ein Parameter nicht vorhanden ist, m&uuml;ssen wir dementsprechend auch einen Teil der Abfrage weglassen. Die dazu notwendigen &Auml;nderungen haben wir in Listing 2 abgebildet.<\/p>\n<pre>ALTER PROCEDURE [dbo].[spGetCustomersWithSpecificProducts](\r\n   @Produkte VARCHAR(255) = NULL,\r\n   @NichtProdukte VARCHAR(255) = NULL,\r\n   @DatumVon VARCHAR(50) = NULL,\r\n   @DatumBis VARCHAR(50) = NULL,\r\n   @Land VARCHAR(50) = NULL\r\n)\r\nAS\r\nBEGIN\r\n&nbsp;&nbsp;&nbsp;&nbsp;SET NOCOUNT ON;\r\n&nbsp;&nbsp;&nbsp;&nbsp;DECLARE @SQL NVARCHAR(MAX);\r\n&nbsp;&nbsp;&nbsp;&nbsp;DECLARE @WHERE NVARCHAR(MAX);\r\n&nbsp;&nbsp;&nbsp;&nbsp;SET @SQL = N''SELECT DISTINCT KundeID, AnredeID, Nachname, Vorname, EMail, Land, Land_Rechnung \r\n&nbsp;&nbsp;&nbsp;&nbsp;FROM tblKunden \r\n&nbsp;&nbsp;&nbsp;&nbsp;WHERE KundeID IN (\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SELECT tblAbonnements.KundeID \r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FROM tblAbonnements''\r\n&nbsp;&nbsp;&nbsp;&nbsp;If @Produkte IS NOT NULL\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SET @WHERE = '' AND tblAbonnements.ProduktID IN ('' + @Produkte + '')'';\r\n&nbsp;&nbsp;&nbsp;&nbsp;IF @DatumVon IS NOT NULL\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SET @WHERE = @WHERE + '' AND Startdatum &gt;= '''''' + @DatumVon + '''''''';\r\n&nbsp;&nbsp;&nbsp;&nbsp;IF @DatumBis IS NOT NULL \r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SET @WHERE = @WHERE + '' AND Startdatum &lt;= '''''' + @DatumBis + '''''''';\r\n&nbsp;&nbsp;&nbsp;&nbsp;IF @WHERE IS NOT NULL\r\n&nbsp;&nbsp;&nbsp;&nbsp;BEGIN\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SET @WHERE = SUBSTRING(@WHERE, 5, LEN(@WHERE));\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SET @SQL = @SQL + '' WHERE '' + @WHERE;\r\n&nbsp;&nbsp;&nbsp;&nbsp;END;\r\n&nbsp;&nbsp;&nbsp;&nbsp;SET @SQL = @SQL + '')''\r\n&nbsp;&nbsp;&nbsp;&nbsp;IF @NichtProdukte IS NOT NULL \r\n&nbsp;&nbsp;&nbsp;&nbsp;BEGIN\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SET @SQL = @SQL + ''AND KundeID NOT IN (\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SELECT tblAbonnements.KundeID \r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FROM tblAbonnements \r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WHERE tblAbonnements.ProduktID IN ('' + @NichtProdukte + '')\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;)'';\r\n&nbsp;&nbsp;&nbsp;&nbsp;END;\r\n&nbsp;&nbsp;&nbsp;&nbsp;IF @Land IS NOT NULL \r\n&nbsp;&nbsp;&nbsp;&nbsp;BEGIN\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SET @SQL = @SQL + ''AND (Land = '''''' + @Land + '''''' \r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OR Land_Rechnung = '''''' + @Land + '''''')'';\r\n&nbsp;&nbsp;&nbsp;&nbsp;END;\r\n&nbsp;&nbsp;&nbsp;&nbsp;EXEC sp_executesql @SQL;\r\nEND\r\nGO<\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 2: Gespeicherte Prozedur mit optionalen Parametern<\/span><\/b><\/p>\n<p>Die erste wichtige &Auml;nderung ist, dass wir f&uuml;r alle optionalen Parameter einen Standardwert angegeben haben &#8211; in diesem Fall lautet dieser jeweils <b>NULL<\/b>:<\/p>\n<pre>ALTER PROCEDURE [dbo].[spGetCustomersWithSpecificProducts](\r\n   @Produkte VARCHAR(255) = NULL,\r\n   @NichtProdukte VARCHAR(255) = NULL,\r\n   @DatumVon VARCHAR(50) = NULL,\r\n   @DatumBis VARCHAR(50) = NULL,\r\n   @Land VARCHAR(50) = NULL\r\n)<\/pre>\n<p>Auf diese Weise k&ouml;nnen wir im Verlauf pr&uuml;fen, ob ein Parameter den Wert <b>NULL <\/b>hat und abh&auml;ngig davon ein bestimmtes Kriterium zur Abfrage hinzuf&uuml;gen oder auch nicht.<\/p>\n<p>Wir wollen in diese Abfrage Kunden ermitteln, die bestimmte Abonnements haben oder auch nicht haben oder die in bestimmten Datumsbereichen liegen.<\/p>\n<p>Die Basisabfrage sieht also so aus:<\/p>\n<pre>SELECT DISTINCT KundeID, AnredeID, Nachname, Vorname, EMail, Land, Land_Rechnung \r\nFROM tblKunden \r\nWHERE KundeID IN (\r\n     SELECT tblAbonnements.KundeID \r\n     FROM tblAbonnements\r\n     ... weitere Kriterien ...\r\n)<\/pre>\n<p>Wenn einer der Parameter <b>@Produkte<\/b>, <b>@NichtProdukte<\/b>, <b>@DatumVon <\/b>oder <b>@DatumBis <\/b>angegeben wurde, soll an der Stelle, wo jetzt <b>&#8230; weitere Kriterien &#8230; <\/b>steht, eine <b>WHERE<\/b>-Bedingung eingebaut werden.<\/p>\n<p>Diese bauen wir in den folgenden Zeilen zusammen. Als Erstes pr&uuml;fen wir, ob <b>@Produkte <\/b>einen Wert hat. Ist das der Fall, f&uuml;gen wir einer Variablen namens <b>@WHERE <\/b>beispielsweise die folgende Zeichenkette hinzu (angenommen, wir haben <b>1,2,3,4,5,6 <\/b>f&uuml;r <b>@Produkte <\/b>&uuml;bergeben):<\/p>\n<pre>AND tblAbonnements.ProduktID IN (1,2,3,4,5,6)<\/pre>\n<p>Dann pr&uuml;fen wir, ob ein Wert mit dem Parameter <b>@DatumVon<\/b> &uuml;bergeben wurde. Ist das der Fall, f&uuml;gen wir an <b>@WHERE <\/b>beispielsweise den folgenden Ausdruck an:<\/p>\n<pre>AND Startdatum &gt;= ''2024-01-01''<\/pre>\n<p>Das Gleiche erledigen wir f&uuml;r den Parameter @DatumBis. Hier lautet der Ausdruck, den wir mit den folgenden Anweisungen anlegen, wie folgt:<\/p>\n<pre>AND Startdatum &lt;= ''2024-12-31''<\/pre>\n<p>Nun hat <b>@WHERE <\/b>entweder einen Wert, der sich aus einem oder mehreren der oben genannten Ausdr&uuml;cke zusammensetzt, oder <b>@WHERE <\/b>ist <b>NULL<\/b>. Wenn <b>@WHERE <\/b>nicht <b>NULL <\/b>ist, beginnt der enthaltene Ausdruck mit dem Schl&uuml;sselwort <b>AND <\/b>(mit f&uuml;hrendem Leerzeichen). Das pr&uuml;fen wir in der folgenden <b>IF<\/b>-Bedingung, wo wir den Ausdruck <b>@WHERE IS NOT NULL <\/b>untersuchen.<\/p>\n<p>Ist <b>@WHERE <\/b>nicht <b>NULL<\/b>, entfernen wir die ersten f&uuml;nf Zeichen aus dem enthaltenen Text. Anschlie&szlig;end f&uuml;gen wir das Schl&uuml;sselwort <b>WHERE <\/b>vorn ein. Wenn wir alle zuvor angegebenen Parameter genutzt haben, sieht der Inhalt von <b>@WHERE <\/b>nun so aus:<\/p>\n<pre>WHERE tblAbonnements.ProduktID IN (1,2,3,4,5,6) AND Startdatum &gt;= ''2024-01-01'' AND Startdatum &lt;= ''2024-12-31''<\/pre>\n<p>Diesen f&uuml;gen wir nun mit der eigentlichen Abfrage wie folgt zusammen, indem wir <b>@WHERE <\/b>an <b>@SQL <\/b>anh&auml;ngen und noch eine schlie&szlig;ende Klammer anf&uuml;gen:<\/p>\n<pre>SELECT DISTINCT KundeID, AnredeID, Nachname, Vorname, EMail, Land, Land_Rechnung    \r\nFROM tblKunden    \r\nWHERE KundeID IN (    \r\n     SELECT tblAbonnements.KundeID     \r\n     FROM tblAbonnements \r\n     WHERE tblAbonnements.ProduktID IN (1,2,3,4,5,6) \r\n     AND Startdatum &gt;= ''2024-01-02'' \r\n     AND Startdatum &lt;= ''2024-12-31'')<\/pre>\n<p>Nun fehlt noch der Teil, der Kunden mit bestimmten Abonnements ausschlie&szlig;en soll. Hier pr&uuml;fen wir einfach, ob <b>@NichtProdukte <\/b>nicht den Wert <b>NULL <\/b>hat.<\/p>\n<p>In diesem Fall f&uuml;gen wir, wenn wir beispielsweise den Ausdruck <b>10,12 <\/b>f&uuml;r <b>@NichtProdukte <\/b>&uuml;bergeben haben, den folgenden Teil zu <b>@SQL <\/b>hinzu:<\/p>\n<pre>AND KundeID NOT IN (\r\n     SELECT tblAbonnements.KundeID \r\n     FROM tblAbonnements \r\n     WHERE tblAbonnements.ProduktID IN (10,12)<\/pre>\n<p>Schlie&szlig;lich fehlt noch der Parameter @Land. Diesen ber&uuml;cksichtigen wir beispielsweise f&uuml;r den Wert <b>Deutschland <\/b>wie folgt:<\/p>\n<pre>AND (Land = ''Deutschland'' OR Land_Rechnung = ''Deutschland'')''<\/pre>\n<p>Schlie&szlig;lich f&uuml;hren wir den Inhalt von <b>@SQL <\/b>mit der <b>EXEC<\/b>-Methode aus:<\/p>\n<pre>EXEC sp_executesql @SQL;<\/pre>\n<h2>Aufruf von gespeicherten Prozeduren mit optionalen Parametern<\/h2>\n<p>Damit k&ouml;nnen wir nun einen oder mehrere Parameter weglassen. Wenn wir die gespeicherte Prozedur mit der <b>EXEC<\/b>-Anweisung aufrufen und einen oder mehrere Parameter weglassen wollen, haben wir verschiedene M&ouml;glichkeiten.<\/p>\n<p>Die sicherste ist, alle Parameter, die &uuml;bergeben werden sollen, als benannte Parameter anzugeben, also beispielsweise so:<\/p>\n<pre>EXEC dbo.spGetCustomersWithSpecificProducts @Produkte=\"1,2,3,4,5,6\", @Land=''DEUTSCHLAND''<\/pre>\n<p>Auf diese Weise stellen wir sicher, dass wir keinen Fehler erhalten, auch wenn die gespeicherte Prozedur zwischenzeitlich um weitere Parameter erg&auml;nzt wird. Die zweite Variante gibt f&uuml;r alle nicht verwendeten Parameter den Wert <b>NULL <\/b>als Parameter an. F&uuml;r das vorherige Beispiel s&auml;he das wie folgt aus:<\/p>\n<pre>EXEC dbo.spGetCustomersWithSpecificProducts \"1,2,3,4,5,6\", NULL, NULL, NULL, ''DEUTSCHLAND''<\/pre>\n<p>Es kann auch sein, dass wir am Ende einen oder mehrere Parameter weglassen wollen.<\/p>\n<p>In diesem Fall brauchen wir den Wert <b>NULL <\/b>nicht explizit anzugeben:<\/p>\n<pre>EXEC dbo.spGetCustomersWithSpecificProducts \"1,2,3,4,5,6\"<\/pre>\n<p>Wir k&ouml;nnen jedoch nicht, wie es beispielsweise beim Aufrufen von Funktionen oder Prozeduren unter VBA m&ouml;glich ist einfach einen Parameterwert zwischen zwei Kommata weglassen. Der folgende Ausdruck w&uuml;rde also nicht funktionieren:<\/p>\n<pre>EXEC dbo.spGetCustomersWithSpecificProducts \"1,2,3,4,5,6\", , , , ''DEUTSCHLAND''<\/pre>\n<h2>Ausgabe des resultierenden SQL-Ausdrucks<\/h2>\n<p>Wenn Fehler auftreten, die wir nicht nachvollziehen k&ouml;nnen, m&ouml;chten wir einen Blick auf den zusammengestellten SQL-Ausdruck werfen.<\/p>\n<p>Diesen k&ouml;nnen wir nach dem Zusammenstellen als eigenes Abfrageergebnis ausgeben lassen:<\/p>\n<pre>SELECT @SQL AS ''Generated SQL Code'';<\/pre>\n<p>Diese Anweisung f&uuml;gen wir einfach als letzte Anweisung in die <b>CREATE PROCEDURE<\/b>&#8211; oder <b>ALTER PROCEDURE<\/b>-Methode ein.<\/p>\n<p>Dies liefert im SQL-Server ein Ergebnis vor dem eigentlichen Ergebnis (siehe Bild 8).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_02\/pic_1491_008.png\" alt=\"Ausgabe des SQL-Ausdrucks\" width=\"700\" height=\"252,0335\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 8: Ausgabe des SQL-Ausdrucks<\/span><\/b><\/p>\n<p>Rufen wir diese Abfrage nun von Access aus auf, erhalten wir nur das Resultat der ersten Abfrage als Ergebnis (siehe Bild 9). Dieses ist wesentlich besser lesbar, da es mit Zeilenumbr&uuml;chen dargestellt wird.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_02\/pic_1491_009.png\" alt=\"Ausgabe in Access\" width=\"549,559\" height=\"244,8621\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 9: Ausgabe in Access<\/span><\/b><\/p>\n<h2>Dynamische Abfrage mit optionalen Parametern von Access aus aufrufen<\/h2>\n<p>Wenn wir diese gespeicherte Abfrage auf Basis von dynamischem SQL von Access aus aufrufen wollen, verwenden wir die gleiche Anweisung in der Pass-Through-Abfrage wie im SQL Server (siehe Bild 10).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_02\/pic_1491_010.png\" alt=\"Aufruf mit optionalen Parametern von Access aus\" width=\"499,5589\" height=\"166,8717\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 10: Aufruf mit optionalen Parametern von Access aus<\/span><\/b><\/p>\n<p>Aber wenn wir diese Abfrage von einem Formular aus zusammenstellen, sodass nur die angegebenen Parameter &uuml;bergeben werden, m&uuml;ssen wir noch ein wenig VBA bem&uuml;hen. Hier pr&uuml;fen wir jeweils, ob der Benutzer einen Wert f&uuml;r den jeweiligen Parameter eingegeben hat und f&uuml;gen zu einer Variablen wie <b>strParameter<\/b> die einzelnen Name-Wert-Paare hinzu.<\/p>\n<pre><span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> IsNull(Me!txtBestelltVom)<span style=\"color:blue;\"> Then<\/span>\r\n     strParameter = strParameter & \", @DatumVon = ''\" _\r\n         & Format(Me.txtBestelltVom, \"yyyy-mm-dd\") & \"''\"\r\n<span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> IsNull(Me!txtBestelltBis)<span style=\"color:blue;\"> Then<\/span>\r\n     strParameter = strParameter & \", @DatumBis = ''\" _\r\n         & Format(Me!txtBestelltBis, \"yyyy-mm-dd\") & \"''\"\r\n<span style=\"color:blue;\">End If<\/span><\/pre>\n<p>Die Zeichenkette f&uuml;r <b>strParameter <\/b>m&uuml;ssen wir dann allerdings noch in die Pass-Through-Abfrage integrieren. Genau genommen k&ouml;nnen wir diese dann auch mit einer komplett neuen <b>EXEC<\/b>-Anweisung f&uuml;llen. Das sieht dann beispielsweise wie in Listing 3 aus. Hier untersuchen wir die einzelnen Steuerelemente des zu Beginn vorgestellten Formulars.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>KundenAktualisieren()\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>strSQL<span style=\"color:blue;\"> As String<\/span>\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>rstSQL<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;\">Dim <\/span>strIN<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> Me!lstBestellteProdukte.ListCount = 0<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">Set<\/span> rst = db.OpenRecordset(\"SELECT ProduktID FROM tblBestellteProdukte\", dbOpenDynaset)\r\n         <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rst.EOF\r\n             strIN = strIN & \", \" & rst!ProduktID\r\n             rst.Move<span style=\"color:blue;\">Next<\/span>\r\n         <span style=\"color:blue;\">Loop<\/span>\r\n         <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> <span style=\"color:blue;\">Len<\/span>(strIN) = 0<span style=\"color:blue;\"> Then<\/span>\r\n             strIN = <span style=\"color:blue;\">Mid<\/span>(strIN, 3)\r\n             strParameter = strParameter & \", @Produkte=''\" & strIN & \"''\"\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     strIN = \"\"\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> Me.lstNichtBestellteProdukte.ListCount = 0<span style=\"color:blue;\"> Then<\/span>\r\n         ''...Zusammenstellen der nicht bestellten Abonnements\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> IsNull(Me!txtBestelltVom)<span style=\"color:blue;\"> Then<\/span>\r\n         strParameter = strParameter & \", @DatumVon = ''\" & Format(Me!txtBestelltVom, \"yyyy-mm-dd\") & \"''\"\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> IsNull(Me!txtBestelltBis)<span style=\"color:blue;\"> Then<\/span>\r\n         strParameter = strParameter & \", @DatumBis = ''\" & Format(Me!txtBestelltBis, \"yyyy-mm-dd\") & \"''\"\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> IsNull(Me!txtLand)<span style=\"color:blue;\"> Then<\/span>\r\n         strParameter = strParameter & \", @Land = ''\" & Me!txtLand & \"''\"\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> <span style=\"color:blue;\">Len<\/span>(strParameter) = 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     strSQL = \"EXEC dbo.spGetCustomersWithSpecificProducts \" & strParameter\r\n     <span style=\"color:blue;\">Set<\/span> qdf = db.QueryDefs(\"qrySPGetCustomersWithSpecificProducts\")\r\n     qdf.SQL = strSQL\r\n     On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> rstSQL = qdf.OpenRecordset(dbOpenDynaset)\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> Err.Number = 0<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">Debug.Print<\/span> DBEngine.Errors(0), DBEngine.Errors(1)\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">On Error GoTo<\/span> 0\r\n     <span style=\"color:blue;\">Set<\/span> Me!sfmKundenNachProduktenFiltern.Form.Recordset = rstSQL\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 3: Zusammenstellen des Aufrufs der gespeicherten Prozedur mit optionalen Parametern<\/span><\/b><\/p>\n<h2>Zusammenfassung und Ausblick<\/h2>\n<p>Komplizierte Abfragen auf Basis von per ODBC verkn&uuml;pften Tabellen, deren Ausf&uuml;hrung sehr lange brauchen, sind ein guter Grund, einen Schritt weiter zu gehen. Im Falle einer SQL Server-Migration hei&szlig;t das: Wir bauen die Abfrage in einer gespeicherten Prozedur nach und rufen diese dann von Access aus auf. Dabei kann es sein, dass wir Parameter &uuml;bergeben m&uuml;ssen.<\/p>\n<p>Dies wird, wie dieser Beitrag zeigt, je nach Komplexit&auml;t zu einer Flei&szlig;arbeit. In diesem Fall sowohl auf Seiten von Access, wo wir die Parameter aus den Benutzereingaben im Formular ermitteln m&uuml;ssen, als auch auf der SQL Server-Seite.<\/p>\n<p>Hier pr&uuml;fen wir, welche Parameter &uuml;bergeben wurden, und integrieren sie in einer dynamisch zusammengestellten SQL-Abfrage. Deren Ergebnis liefern wir dann als Recordset an die aufrufende Access-Instanz zur&uuml;ck.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Die einfache Migration der Tabellen einer Datenbank von Access zum SQL Server ist meist schnell erledigt. Der SQL Server Migration Assistant (SSMA) leistet gute Arbeit und schnell landen alle Tabellen in der SQL Server-Datenbank &#8211; samt Erstellung entsprechender Tabellenverkn&uuml;pfungen im Access-Frontend. Damit l&auml;sst sich erst einmal arbeiten, da die Verkn&uuml;pfungen Lesen und Schreiben der Daten wie gewohnt zulassen. Fr&uuml;her oder sp&auml;ter wird man jedoch auf Abfragen im Access-Frontend sto&szlig;en, die schlicht zu langsam sind. Und hier kann der SQL Server sein wahres Potential ausspielen: Zum Beispiel, indem wir dort eine Stored Procedure (Gespeicherte Abfrage) anlegen, welche die Abfrage f&uuml;r uns direkt auf dem SQL Server ausf&uuml;hrt, was viel schneller gehen wird. Und genau das ist Thema dieses Beitrags: Wie bekommen wir eine mehr oder weniger komplizierte Abfrage als Stored Procedure zum SQL Server und rufen diese von der Access-Datenbank aus auf?<\/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":[66022024,662024,44000022],"tags":[],"class_list":["post-55001491","post","type-post","status-publish","format-standard","hentry","category-66022024","category-662024","category-SQL_Server_und_Co"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v20.9 (Yoast SEO v27.4) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>SQL Server: Von der Abfrage zur Stored Procedure - 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\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"SQL Server: Von der Abfrage zur Stored Procedure\" \/>\n<meta property=\"og:description\" content=\"Die einfache Migration der Tabellen einer Datenbank von Access zum SQL Server ist meist schnell erledigt. Der SQL Server Migration Assistant (SSMA) leistet gute Arbeit und schnell landen alle Tabellen in der SQL Server-Datenbank - samt Erstellung entsprechender Tabellenverkn&uuml;pfungen im Access-Frontend. Damit l&auml;sst sich erst einmal arbeiten, da die Verkn&uuml;pfungen Lesen und Schreiben der Daten wie gewohnt zulassen. Fr&uuml;her oder sp&auml;ter wird man jedoch auf Abfragen im Access-Frontend sto&szlig;en, die schlicht zu langsam sind. Und hier kann der SQL Server sein wahres Potential ausspielen: Zum Beispiel, indem wir dort eine Stored Procedure (Gespeicherte Abfrage) anlegen, welche die Abfrage f&uuml;r uns direkt auf dem SQL Server ausf&uuml;hrt, was viel schneller gehen wird. Und genau das ist Thema dieses Beitrags: Wie bekommen wir eine mehr oder weniger komplizierte Abfrage als Stored Procedure zum SQL Server und rufen diese von der Access-Datenbank aus auf?\" \/>\n<meta property=\"og:url\" content=\"https:\/\/access-im-unternehmen.de\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\/\" \/>\n<meta property=\"og:site_name\" content=\"Access im Unternehmen\" \/>\n<meta property=\"article:published_time\" content=\"2024-02-29T19:02:54+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/vg02.met.vgwort.de\/na\/5f008ae4b2a940bcb34e4024370481cf\" \/>\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=\"22\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\\\/\"},\"author\":{\"name\":\"Andr\u00e9 Minhorst\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\"},\"headline\":\"SQL Server: Von der Abfrage zur Stored Procedure\",\"datePublished\":\"2024-02-29T19:02:54+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\\\/\"},\"wordCount\":3184,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg02.met.vgwort.de\\\/na\\\/5f008ae4b2a940bcb34e4024370481cf\",\"articleSection\":[\"2\\\/2024\",\"2024\",\"SQL Server und Co.\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\\\/\",\"name\":\"SQL Server: Von der Abfrage zur Stored Procedure - Access im Unternehmen\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg02.met.vgwort.de\\\/na\\\/5f008ae4b2a940bcb34e4024370481cf\",\"datePublished\":\"2024-02-29T19:02:54+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\\\/#primaryimage\",\"url\":\"http:\\\/\\\/vg02.met.vgwort.de\\\/na\\\/5f008ae4b2a940bcb34e4024370481cf\",\"contentUrl\":\"http:\\\/\\\/vg02.met.vgwort.de\\\/na\\\/5f008ae4b2a940bcb34e4024370481cf\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"SQL Server: Von der Abfrage zur Stored Procedure\"}]},{\"@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":"SQL Server: Von der Abfrage zur Stored Procedure - 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\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\/","og_locale":"de_DE","og_type":"article","og_title":"SQL Server: Von der Abfrage zur Stored Procedure","og_description":"Die einfache Migration der Tabellen einer Datenbank von Access zum SQL Server ist meist schnell erledigt. Der SQL Server Migration Assistant (SSMA) leistet gute Arbeit und schnell landen alle Tabellen in der SQL Server-Datenbank - samt Erstellung entsprechender Tabellenverkn&uuml;pfungen im Access-Frontend. Damit l&auml;sst sich erst einmal arbeiten, da die Verkn&uuml;pfungen Lesen und Schreiben der Daten wie gewohnt zulassen. Fr&uuml;her oder sp&auml;ter wird man jedoch auf Abfragen im Access-Frontend sto&szlig;en, die schlicht zu langsam sind. Und hier kann der SQL Server sein wahres Potential ausspielen: Zum Beispiel, indem wir dort eine Stored Procedure (Gespeicherte Abfrage) anlegen, welche die Abfrage f&uuml;r uns direkt auf dem SQL Server ausf&uuml;hrt, was viel schneller gehen wird. Und genau das ist Thema dieses Beitrags: Wie bekommen wir eine mehr oder weniger komplizierte Abfrage als Stored Procedure zum SQL Server und rufen diese von der Access-Datenbank aus auf?","og_url":"https:\/\/access-im-unternehmen.de\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\/","og_site_name":"Access im Unternehmen","article_published_time":"2024-02-29T19:02:54+00:00","og_image":[{"url":"http:\/\/vg02.met.vgwort.de\/na\/5f008ae4b2a940bcb34e4024370481cf","type":"","width":"","height":""}],"author":"Andr\u00e9 Minhorst","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Andr\u00e9 Minhorst","Gesch\u00e4tzte Lesezeit":"22\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/access-im-unternehmen.de\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\/#article","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\/"},"author":{"name":"Andr\u00e9 Minhorst","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f"},"headline":"SQL Server: Von der Abfrage zur Stored Procedure","datePublished":"2024-02-29T19:02:54+00:00","mainEntityOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\/"},"wordCount":3184,"commentCount":0,"publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\/#primaryimage"},"thumbnailUrl":"http:\/\/vg02.met.vgwort.de\/na\/5f008ae4b2a940bcb34e4024370481cf","articleSection":["2\/2024","2024","SQL Server und Co."],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/access-im-unternehmen.de\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/access-im-unternehmen.de\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\/","url":"https:\/\/access-im-unternehmen.de\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\/","name":"SQL Server: Von der Abfrage zur Stored Procedure - Access im Unternehmen","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\/#primaryimage"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\/#primaryimage"},"thumbnailUrl":"http:\/\/vg02.met.vgwort.de\/na\/5f008ae4b2a940bcb34e4024370481cf","datePublished":"2024-02-29T19:02:54+00:00","breadcrumb":{"@id":"https:\/\/access-im-unternehmen.de\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/access-im-unternehmen.de\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\/#primaryimage","url":"http:\/\/vg02.met.vgwort.de\/na\/5f008ae4b2a940bcb34e4024370481cf","contentUrl":"http:\/\/vg02.met.vgwort.de\/na\/5f008ae4b2a940bcb34e4024370481cf"},{"@type":"BreadcrumbList","@id":"https:\/\/access-im-unternehmen.de\/SQL_Server_Von_der_Abfrage_zur_Stored_Procedure\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/access-im-unternehmen.de\/"},{"@type":"ListItem","position":2,"name":"SQL Server: Von der Abfrage zur Stored Procedure"}]},{"@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\/55001491","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=55001491"}],"version-history":[{"count":0,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55001491\/revisions"}],"wp:attachment":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/media?parent=55001491"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/categories?post=55001491"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/tags?post=55001491"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}