{"id":55000911,"date":"2013-12-01T00:00:00","date_gmt":"2020-05-22T21:31:36","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=911"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"Aktionsabfragen_statt_Schleifen","status":"publish","type":"post","link":"https:\/\/access-im-unternehmen.de\/Aktionsabfragen_statt_Schleifen\/","title":{"rendered":"Aktionsabfragen statt Schleifen"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg05.met.vgwort.de\/na\/94285e3006074741932a61ef6eb8550d\" width=\"1\" height=\"1\" alt=\"\"><\/p>\n<p><b>&auml;nderungen an den Daten in den Tabellen einer Datenbank kann man auf verschiedenste Arten durchf&uuml;hren &#8211; zum Beispiel &uuml;ber die Benutzeroberfl&auml;che. Gelegentlich werden Sie jedoch auch Daten per VBA &auml;ndern wollen. In diesem Falle gibt es eine ganze Reihe von Varianten, die normalerweise mit den Methoden der DAO-Bibliotheken abgebildet werden: Das &ouml;ffnen eines Recordsets und &auml;ndern der enthaltenen Daten mit AddNew\/Edit und Update, das Zusammenstellen einer SQL-Aktionsabfrage, die dann mit der Execute-Methode des Database-Objekts ausgef&uuml;hrt wird, oder der Einsatz gespeicherter Aktionsabfragen, die man mit der Execute-Methode des QueryDef-Objekts startet. Wir schauen uns die verschiedenen Varianten an und optimieren diese.<\/b><\/p>\n<p><b>Performance und Stil<\/b><\/p>\n<p>Der Grund f&uuml;r diesen Beitrag ist offensichtlich: Die verschiedenen Methoden zum &auml;ndern von Daten unterscheiden sich stark in der Performance. Wenn Sie eine Schleife &uuml;ber eine Datensatzgruppe durchlaufen und daraus entnommene Daten mit den DAO-Methoden <b>AddNew <\/b>und <b>Update <\/b>zu einem weiteren Recordset hinzuf&uuml;gen, dauert das schlicht und einfach l&auml;nger als wenn Sie die gesamte Prozedur in einer einzigen Aktionsabfrage unterbringen.<\/p>\n<p>Und das ist nicht nur das Bild, das man bei der Unterst&uuml;tzung anderer Entwickler vorfindet, sondern man selbst neigt ebenso dazu, erstmal den scheinbar einfacheren Weg &uuml;ber DAO zu w&auml;hlen, bevor man auf den ersten Blick kompliziert erscheinende Aktionsabfragen zusammenstellt.<\/p>\n<p>Und damit landen wir gleich beim anderen Aspekt: In vielen F&auml;llen kostet das Entwickeln einer eleganten, performanten L&ouml;sung schlicht und einfach mehr Zeit als das stumpfe Herunterprogrammieren bekannter Codestrukturen &#8211; zumindest wenn man nicht &uuml;ber ausreichend Programmiererfahrung verf&uuml;gt.<\/p>\n<p>Dann hat man n&auml;mlich oft genug erst Datensatz f&uuml;r Datensatz mit DAO durchpfl&uuml;gt, bevor man den ersten Entwurf dann in einem oder mehreren Schritten zu einer zufriedenstellenden L&ouml;sung weiterentwickelt hat, die sowohl den Anspruch an die Performance als auch an die Eleganz befriedigt.<\/p>\n<p>Eine solche Entwicklung k&ouml;nnen Sie in der L&ouml;sung zum Beitrag <b>Datenmodelle vergleichen <\/b>(<b>www.access-im-unternehmen.de\/916<\/b>) nachvollziehen. Im vorliegenden Beitrag schauen wir uns ein etwas einfacheres Beispiel an und f&uuml;hren die Schritte vom ersten Ansatz bis zur perfekten L&ouml;sung durch.<\/p>\n<p><b>Beispiel: Kundendaten archivieren<\/b><\/p>\n<p>Im Beispiel dieses Beitrags wollen wir einfach bestimmte Kundendaten in eine andere Tabelle kopieren &#8211; beispielsweise, um diese zu archivieren. Die Kundendaten sollen nach bestimmten Kriterien ausgew&auml;hlt werden &#8211; beispielsweise nach dem Zeitpunkt der letzten Bestellung. Liegt diese mehr als ein Jahr zur&uuml;ck, soll der Kunde in eine entsprechende Tabelle kopiert werden.<\/p>\n<p>Au&szlig;erdem soll die neue Tabelle noch ein weiteres Feld mit dem Datum der Archivierung enthalten (s. Bild 1).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2013_06\/pic_911_001.png\" alt=\"Quell- und Zieltabelle\" width=\"575\" height=\"263,253\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Quell- und Zieltabelle<\/span><\/b><\/p>\n<p>Daten mit bestimmten Kriterien aus einer Tabelle in eine weitere Tabelle kopieren, die &uuml;berdies noch um weitere Daten erg&auml;nzt werden soll &#8211; das h&ouml;rt sich sehr kompliziert an. Also gehen wir das Ganze mal sch&ouml;n langsam Schritt f&uuml;r Schritt an &#8211; so, dass man zu jeder Zeit kontrollieren kann, was dort geschieht.<\/p>\n<p><b>Start der Archivierung<\/b><\/p>\n<p>Die Archivierung soll &uuml;ber Schaltfl&auml;chen eines Formulars namens <b>frmKundenArchivieren<\/b> gestartet werden (s. Bild 2).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2013_06\/pic_911_011.png\" alt=\"Formular mit Schaltfl&auml;chen zum Starten der verschiedenen Archivierungen\" width=\"450\" height=\"448,2422\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Formular mit Schaltfl&auml;chen zum Starten der verschiedenen Archivierungen<\/span><\/b><\/p>\n<p>Dieses Formular ruft die verschiedenen Varianten der Archivierung auf und &uuml;bergibt dabei jeweils das im Textfeld <b>txtStichtag <\/b>gespeicherte Datum an die <b>Funktionen<\/b>, f&uuml;r die erste Schaltfl&auml;che etwa so:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmd1_Click()\r\n     <span style=\"color:blue;\">Dim <\/span>intAnzahl<span style=\"color:blue;\"> As Integer<\/span>\r\n     intAnzahl = Datenkopieren_ DAOMitAbfrage(Me!txtStichtag)\r\n     <span style=\"color:blue;\">MsgBox<\/span> intAnzahl _\r\n         & \" Datens&auml;tze archiviert\"\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b>Die &#8222;sichere&#8220; Variante<\/b><\/p>\n<p>Wer noch nicht erfahren im Umgang mit Access ist, hat vielleicht noch nicht mit SQL gearbeitet und wird dementsprechend zun&auml;chst eine Abfrage wie oben beschrieben erstellen.<\/p>\n<p>Dann wird er eine VBA-Funktion bauen, die wie die in Listing 1 aussieht. Sie sucht nach Kunden, die seit dem per Parameter &uuml;bergebenen Stichtag keine Bestellung mehr durchgef&uuml;hrt haben und archiviert werden sollen, und liefert die Anzahl der betroffenen Datens&auml;tze zur&uuml;ck.<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>Datenkopieren_DAOMitAbfrage(datStichtag<span style=\"color:blue;\"> As Date<\/span>)<span style=\"color:blue;\"> As Integer<\/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>rstQuelle<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Dim <\/span>rstZiel<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Dim <\/span>datLetzteBestellung<span style=\"color:blue;\"> As Date<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>intAnzahl<span style=\"color:blue;\"> As Integer<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> rstQuelle = db.OpenRecordset(\"SELECT * FROM tblKunden\", dbOpenDynaset)\r\n     <span style=\"color:blue;\">Set<\/span> rstZiel = db.OpenRecordset(\"SELECT * FROM tblKunden_Archiv WHERE 1 = 2\", dbOpenDynaset)\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rstQuelle.EOF\r\n         datLetzteBestellung = Nz(DMax(\"Bestelldatum\", \"tblBestellungen\", _\r\n             \"KundeID = \" & rstQuelle!KundeID), 0)\r\n         <span style=\"color:blue;\">If <\/span>datLetzteBestellung &lt; datStichtag<span style=\"color:blue;\"> Then<\/span>\r\n             <span style=\"color:blue;\">If <\/span>IsNull(DLookup(\"KundeID\", \"tblKunden_Archiv\", \"KundeID = \" & rstQuelle!KundeID))<span style=\"color:blue;\"> Then<\/span>\r\n                 <span style=\"color:blue;\">With<\/span> rstZiel\r\n                     .Add<span style=\"color:blue;\">New<\/span>\r\n                     !KundeID = rstQuelle!KundeID\r\n                     !KundenCode = rstQuelle!KundenCode\r\n                     !Firma = rstQuelle!Firma\r\n                     !Kontaktperson = rstQuelle!Kontaktperson\r\n                     !Strasse = rstQuelle!Strasse\r\n                     !Ort = rstQuelle!Ort\r\n                     !Region = rstQuelle!Region\r\n                     !PLZ = rstQuelle!PLZ\r\n                     !Land = rstQuelle!Land\r\n                     !Telefon = rstQuelle!Telefon\r\n                     !Telefax = rstQuelle!Telefax\r\n                     !ArchiviertAm = Now\r\n                     .Update\r\n                     intAnzahl = intAnzahl + 1\r\n                 End <span style=\"color:blue;\">With<\/span>\r\n             <span style=\"color:blue;\">End If<\/span>\r\n         <span style=\"color:blue;\">End If<\/span>\r\n         rstQuelle.Move<span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n     Datenkopieren_DAOMitAbfrage = intAnzahl\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Die &#8222;sichere&#8220; Variante zum Kopieren von Daten<\/span><\/b><\/p>\n<p>Die Funktion erstellt ein <b>Database<\/b>-Objekt namens <b>db <\/b>sowie zwei Datensatzgruppen namens <b>rstQuelle <\/b>und <b>rstZiel<\/b>. Das <b>Recordset<\/b>-Objekt <b>rstQuelle <\/b>wird mit einem Verweis auf alle Datens&auml;tze der Tabelle <b>tblKunden <\/b>gef&uuml;llt, das <b>Recordset<\/b>-Objekt <b>rstZiel <\/b>mit einer Datenherkunft, die auf der Zieltabelle <b>tblKunden_Archiv <\/b>basiert, aber wegen des Kriteriums <b>1=2 <\/b>keine Datens&auml;tze enth&auml;lt.<\/p>\n<p>Die Prozedur durchl&auml;uft nun alle Datens&auml;tze des Recordsets <b>tblKunden<\/b>. Dabei ermittelt sie zun&auml;chst mit dem Aufruf von <b>DLookup <\/b>das aktuellste Datum einer Bestellung aus der Tabelle <b>tblBestellungen <\/b>f&uuml;r die Datens&auml;tze, deren Feld <b>KundeID <\/b>mit dem Kunden des aktuellen Datensatzes aus <b>rstQuelle <\/b>&uuml;bereinstimmt.<\/p>\n<p>F&uuml;r den Fall, dass f&uuml;r den Kunden noch gar keine Bestellung vorliegt, soll <b>datLetzteBestellung <\/b>den Wert <b>0 <\/b>erhalten, was die <b>Nz<\/b>-Funktion mit entsprechendem zweiten Parameter bewerkstelligt.<\/p>\n<p>Die folgende <b>If&#8230;Then<\/b>-Bedingung pr&uuml;ft, ob das in <b>datLetzteBestellung <\/b>gespeicherte Datum kleiner als <b>datStichtag <\/b>ist. Nur in diesem Fall geht es weiter, und zwar mit einer weiteren Pr&uuml;fung: Wenn der Kunde bereits in der Archivtabelle gespeichert ist, soll dieser nicht erneut gespeichert werden.<\/p>\n<p>Erst danach wird der neue Datensatz angelegt, und zwar mit der <b>AddNew<\/b>-Methode des <b>Recordset<\/b>-Objekts <b>rstZiel<\/b>. Die folgenden Anweisungen tragen jeweils den Wert eines Feldes der Quelltabelle in das entsprechende Feld der Zieltabelle ein.<\/p>\n<p>Danach f&uuml;gt die Funktion noch dem Feld <b>ArchiviertAm <\/b>das aktuelle Datum und die aktuelle Uhrzeit hinzu. Die <b>Update<\/b>-Methode sorgt f&uuml;r das Speichern des neu angelegten Datensatzes in der Tabelle <b>tblKunden_Archiv<\/b>.<\/p>\n<p>Schlie&szlig;lich erh&ouml;ht die Prozedur den Wert der Variablen <b>intAnzahl<\/b>, welche die Anzahl der &uuml;bertragenen Datens&auml;tze z&auml;hlen soll, um <b>1<\/b>. Die <b>MoveNext<\/b>-Methode bewegt den Datensatzzeiger zum n&auml;chsten Datensatz, die <b>Loop<\/b>-Anweisung l&auml;sst die Funktion mit einem weiteren Durchlauf der <b>Do While<\/b>-Schleife beginnen.<\/p>\n<p>Schlie&szlig;lich liefert die Funktion die in <b>intAnzahl <\/b>gespeicherte Anzahl betroffener Datens&auml;tze zur&uuml;ck.<\/p>\n<p>Wenn Sie in Betracht ziehen, dass eine solche Vorgehensweise auf gro&szlig;e Datenmengen angewendet wird, sind eine Menge Codezeilen abzuarbeiten, bis das gew&uuml;nschte Resultat erreicht wird. Also k&uuml;mmern wir uns darum, dies zu optimieren. Gleichwohl ist zu erw&auml;hnen, dass die hier eingesetzte Vorgehensweise nicht v&ouml;llig abwegig ist. Erstens kommt sie fast komplett ohne den Einsatz von SQL-Anweisungen aus, was f&uuml;r viele Einsteiger erst in sp&auml;teren Schritten folgt. Somit kommt man mit den grundlegenden DAO-Anweisungen sowie Dom&auml;nenfunktionen &uuml;ber die Runden.<\/p>\n<p>Das Entscheidende f&uuml;r den Entwickler dieser Zeilen mag aber sein, dass sich zu jeder Zeit im Debug-Modus beobachten l&auml;sst, was dort geschieht. Dies ist beim Einsatz etwa von SQL-Aktionsabfragen nur eingeschr&auml;nkt m&ouml;glich.<\/p>\n<p><b>Optimierung 1: DMax aufl&ouml;sen<\/b><\/p>\n<p>Die obige Funktion muss wirklich viel Arbeit leisten, weil sie jeden einzelnen Datensatz der Herkunftstabelle <b>tblKunden <\/b>durchl&auml;uft und diese dahingehend pr&uuml;ft, wie lange die letzte Bestellung des Kunden zur&uuml;ckliegt und ob dieser Datensatz bereits in der Tabelle <b>tblKunden_Archiv <\/b>gespeichert ist. Dabei ist f&uuml;r jeden Datensatz der Aufruf der <b>DMax<\/b>-Funktion f&auml;llig.<\/p>\n<p>Wir wollen in kleinen Schritten optimieren und versuchen zun&auml;chst, die <b>DMax<\/b>-Funktion aus der Schleife herauszunehmen. Die Datenherkunft besteht bisher aus der Tabelle <b>tblKunden<\/b>. Deren Feld <b>KundeID <\/b>verwenden wir, um per <b>DMax<\/b> das Datum der letzten Bestellung zu ermitteln.<\/p>\n<p>Wie bekommen wir nun die <b>DMax<\/b>-Anweisung aus der <b>Do While<\/b>-Schleife heraus Der erste Schritt dazu ist einfach: Wir erstellen einfach eine Abfrage auf Basis der Tabelle <b>tblKunden <\/b>und f&uuml;gen ein weiteres Feld hinzu, welches das Datum der neuesten Bestellung ermittelt &#8211; dies wiederum durch den Einsatz der <b>DMax<\/b>-Funktion. Der Ausdruck f&uuml;r dieses Feld sieht so aus, die komplette Abfrage <b>qryKundenNachBestelldatum <\/b>finden Sie in Bild 3:<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2013_06\/pic_911_003.png\" alt=\"Abfrage basierend auf der Tabelle tblKunden und der neuesten Bestellung f&uuml;r diesen Kunden\" width=\"700\" height=\"326,3201\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 3: Abfrage basierend auf der Tabelle tblKunden und der neuesten Bestellung f&uuml;r diesen Kunden<\/span><\/b><\/p>\n<pre>NeuesteBestellung: Nz(DomMax(\"Bestell-datum\";\"tblBestellungen\";\"KundeID = \" _\r\n    & [KundeID]);0)<\/pre>\n<p>Der Clou hierbei ist, dass die <b>DMax<\/b>-Funktion die ID des Kunden, f&uuml;r den die neueste Bestellung ermittelt werden soll, aus dem Feld<b> KundeID <\/b>der Datenherkunft bezieht.<\/p>\n<p>Die Abfrage <b>qryKundenNachBestelldatum <\/b>nutzen wir nun als Datenherkunft f&uuml;r die zu &uuml;bertragenen Daten. Das Feld <b>NeuesteBestellung <\/b>nutzen wir nun direkt als Kriterium der <b>If&#8230;Then<\/b>-Bedingung, anstatt diesen Wert erst noch in der Prozedur per <b>DMax <\/b>ermitteln zu m&uuml;ssen (s. Listing 2).<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>Datenkopieren_II(datStichtag<span style=\"color:blue;\"> As Date<\/span>)<span style=\"color:blue;\"> As Integer<\/span>\r\n     ...\r\n     <span style=\"color:blue;\">Set<\/span> rstQuelle = db.OpenRecordset(\"SELECT * FROM qryKundenNachBestelldatum\", dbOpenDynaset)\r\n     <span style=\"color:blue;\">Set<\/span> rstZiel = db.OpenRecordset(\"SELECT * FROM tblKunden_Archiv WHERE 1 = 2\", dbOpenDynaset)\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rstQuelle.EOF\r\n         <span style=\"color:blue;\">If <\/span>rstQuelle!NeuesteBestellung &lt; datStichtag<span style=\"color:blue;\"> Then<\/span>\r\n             ...\r\n     Datenkopieren_II = intAnzahl\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><!--30percent--><\/p>\n<p><b><span style=\"color:darkgrey;\">Listing 2: Optimierung der Schleife durch &uuml;bertragen von DMax in die Datenherkunft <\/span><\/b><\/p>\n<p><b>Zeit messen<\/b><\/p>\n<p>Nachdem wir nun eine erste vermeintliche Verbesserung angewendet haben, wollen wir nat&uuml;rlich auch wissen, ob sich dies auf die Performance auswirkt.<\/p>\n<p>Die Zeit zwischen zwei Ereignissen messen Sie unter Windows am genauesten mit den beiden API-Funktionen <b>QueryPerformanceFrequency <\/b>und <b>QueryPerformanceCounter<\/b>. Die Erste liefert die Frequenz, die Zweite den Z&auml;hlerstand des Prozessors. Wenn Sie diesen einmal beim Start der Messung einlesen und einmal zum Ende, brauchen Sie nur noch die Differenz durch die Frequenz zu teilen und erhalten die Zeit in Sekunden.<\/p>\n<p>In Listing 3 werden zun&auml;chst die beiden API-Funktionen deklariert, au&szlig;erdem drei <b>Currency<\/b>-Variablen zum Speichern der Frequenz, des Z&auml;hlerstands beim Start und des Z&auml;hlerstands nach Beenden der zu messenden Vorg&auml;nge. Die Prozedur, die beim Anklicken der Schaltfl&auml;chen zum Starten der verschiedenen Varianten unseres Vorgangs ausgel&ouml;st wird, erledigt nun folgende Aufgaben:<\/p>\n<pre><span style=\"color:blue;\">Private <\/span>Declare Function QueryPerformanceCounter Lib \"Kernel32\" (X<span style=\"color:blue;\"> As Currency<\/span>)<span style=\"color:blue;\"> As Long<\/span>\r\n<span style=\"color:blue;\">Private <\/span>Declare Function QueryPerformanceFrequency Lib \"Kernel32\" (Y<span style=\"color:blue;\"> As Currency<\/span>)<span style=\"color:blue;\"> As Long<\/span>\r\n<span style=\"color:blue;\">Dim <\/span>curFreq<span style=\"color:blue;\"> As Currency<\/span>\r\n<span style=\"color:blue;\">Dim <\/span>curStart<span style=\"color:blue;\"> As Currency<\/span>\r\n<span style=\"color:blue;\">Dim <\/span>curEnde<span style=\"color:blue;\"> As Currency<\/span>\r\n<span style=\"color:blue;\">Private Sub <\/span>cmd1_Click()\r\n     <span style=\"color:blue;\">Dim <\/span>intAnzahl<span style=\"color:blue;\"> As Integer<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     db.Execute \"DELETE FROM tblKunden_Archiv\", dbFailOnError\r\n     QueryPerformanceFrequency curFreq\r\n     QueryPerformanceCounter curStart\r\n     intAnzahl = Datenkopieren_I(Me!txtStichtag)\r\n     QueryPerformanceCounter curEnde\r\n     Me!txtZeitI = (curEnde - curStart) \/ curFreq\r\n     <span style=\"color:blue;\">MsgBox<\/span> intAnzahl & \" Datens&auml;tze archiviert\"\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 3: Einrichtung zum Messen der Zeit<\/span><\/b><\/p>\n<ul>\n<li>Leeren der Archivtabelle <b>tblKunden_Archiv<\/b>, damit immer gleich viele Datens&auml;tze &uuml;bertragen werden<\/li>\n<li>Einlesen der Taktfrequenz des Prozessors<\/li>\n<li>Speichern des Z&auml;hlers vor Beginn des zu messenden Vorgangs<\/li>\n<li>Start des Vorgangs<\/li>\n<li>Speichern des Z&auml;hlers nach dem Vorgang<\/li>\n<li>Ausgabe der verstrichenen Zeit in Sekunden im Textfeld neben der Schaltfl&auml;che <\/li>\n<\/ul>\n<p>Diese Zeilen f&uuml;gen wir jeder Schaltfl&auml;che hinzu, nur der Aufruf der Funktion sowie das Textfeld f&uuml;r die Ausgabe der Zeit &auml;ndert sich. Wie Bild 4 zeigt, hat sich die erste Ma&szlig;nahme durchaus gelohnt: Wir sparen rund 10% der Zeit gegen&uuml;ber der ersten Variante ein.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2013_06\/pic_911_004.png\" alt=\"Ergebnis der ersten beiden Varianten\" width=\"575\" height=\"257,4128\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 4: Ergebnis der ersten beiden Varianten<\/span><\/b><\/p>\n<p>Optimierung II: <\/p>\n<p>Nun f&uuml;hren wir allerdings immer noch f&uuml;r jeden Datensatz einen Aufruf der <b>DMax<\/b>-Funktion aus. Wie k&ouml;nnen wir dies verhindern Eine M&ouml;glichkeit w&auml;re, statt der <b>DMax<\/b>-Funktion mit einer Unterabfrage zu arbeiten. Diese formulieren wir in einer neuen Abfrage namens <b>qryKundenNachBestelldatumMitGruppierung <\/b>wie folgt (siehe auch Bild 5):<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2013_06\/pic_911_005.png\" alt=\"Abfrage mit Unterabfrage\" width=\"700\" height=\"303,6412\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 5: Abfrage mit Unterabfrage<\/span><\/b><\/p>\n<pre>NeuesteBestellung: (SELECT TOP 1 Bestelldatum FROM tblBestellungen \r\nWHERE tblBestellungen.KundeID = tblKunden.KundeID)<\/pre>\n<p>Nach dem Anlegen einer neuen Schaltfl&auml;che im Formular <b>frmKundenArchivieren <\/b>und dem Kopieren der Funktion <b>Datenkopieren_II<\/b> in eine neue Funktion namens <b>Datenkopieren_III <\/b>sehen wir uns an, was diese Optimierung bringt.<\/p>\n<p>Und das Ergebnis ist beeindruckend: Wir sparen rund 97% gegen&uuml;ber der Startkonfiguration! Aber Vorsicht: Zum Gl&uuml;ck lassen wir immer noch die Anzahl der gespeicherten Datens&auml;tze mitlaufen. Und dieses gibt nun an, statt der vorhandenen 1456 Artikel nur noch 89 Datens&auml;tze archiviert zu haben. Haben wir vergessen, die schon archivierten Datens&auml;tze vorher zu l&ouml;schen Nein. Aber wo liegt der Fehler Ganz einfach: Im Gegensatz zum Ausdruck <\/p>\n<pre>NeuesteBestellung: Nz(DomMax(\"Bestell-datum\";\"tblBestellungen\";\"KundeID = \" & [KundeID]);0)<\/pre>\n<p>liefert die Unterabfrage nicht den Wert <b>0<\/b>, sondern den Wert <b>Null<\/b>, wenn noch keine Bestellung f&uuml;r den Kunden vorliegt. Dies m&uuml;ssten wir nun in Form einer <b>IIf<\/b>-Funktion oder &auml;hnlich in der Abfrage unterbringen.<\/p>\n<p>Vorerst wollen wir uns damit begn&uuml;gen, den Vergleich des Feldes <b>NeuesteBestellung <\/b>in der Prozedur mit der <b>Nz<\/b>-Funktion zu versehen, sodass im Falle eines <b>Null<\/b>-Wertes der Wert <b>0 <\/b>angenommen wird:<\/p>\n<pre><span style=\"color:blue;\">If <\/span>Nz(rstQuelle!NeuesteBestellung, 0) &lt; datStichtag<span style=\"color:blue;\"> Then<\/span><\/pre>\n<p>Nach einem weiteren Test liefert dies immerhin eine Zeit von ca. 2,1 Sekunden und somit eine Verbesserung von knapp 30%.<\/p>\n<p><b>Optimierung III: AddNew durch INSERT INTO ersetzen<\/b><\/p>\n<p>Bei dieser Optimierung wollen wir die <b>AddNew<\/b>-Methode, die <b>Update<\/b>-Methode und die vielen einzelnen Anweisungen zum Zuweisen der Feldwerte von der einen an die andere Tabelle ersetzen. Wir arbeiten also weiter mit der Abfrage <b>qryKundenNachBestellungMitGruppierung<\/b>, aber ersetzen eine ganze Reihe Anweisungen durch eine einzige.<\/p>\n<p>Das Ergebnis sieht wie in Listing 4 aus. Die Variable f&uuml;r das Recordset <b>rstZiel<\/b> und alle damit verkn&uuml;pften Anweisungen fallen hier weg. Daf&uuml;r kommt eine etwas l&auml;ngliche <b>INSERT INTO<\/b>-Anweisung hinzu, die mit der <b>Execute<\/b>-Methode des <b>Database<\/b>-Objekts <b>db <\/b>ausgef&uuml;hrt wird.<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>Datenkopieren_III(datStichtag<span style=\"color:blue;\"> As Date<\/span>)<span style=\"color:blue;\"> As Integer<\/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>rstQuelle<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Dim <\/span>intAnzahl<span style=\"color:blue;\"> As Integer<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> rstQuelle = db.OpenRecordset(\"SELECT * FROM qryKundenNachBestelldatumMitGruppierung\", dbOpenDynaset)\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rstQuelle.EOF\r\n         <span style=\"color:blue;\">If <\/span>Nz(rstQuelle!NeuesteBestellung, 0) &lt; datStichtag<span style=\"color:blue;\"> Then<\/span>\r\n             <span style=\"color:blue;\">If <\/span>IsNull(DLookup(\"KundeID\", \"tblKunden_Archiv\", \"KundeID = \" & rstQuelle!KundeID))<span style=\"color:blue;\"> Then<\/span>\r\n                 db.Execute \"INSERT INTO tblKunden_Archiv(KundeID, KundenCode, Firma, Kontaktperson, Strasse, &quot; _                     &amp; &quot;Ort, Region,  PLZ, Land, Telefon, Telefax, ArchiviertAm) VALUES(\" & rstQuelle!KundeID _\r\n                     & \", ''\" & rstQuelle!KundenCode  & \"'', ''\" & <span style=\"color:blue;\">Replace<\/span>(rstQuelle!Firma, \"''\", \"''''\") & \"'', ''\" _\r\n                     & rstQuelle!Kontaktperson & \"'', ''\" & <span style=\"color:blue;\">Replace<\/span>( rstQuelle!Strasse,\"''\", \"''''\") & \"'', ''\" _\r\n                     & rstQuelle!Ort & \"'', ''\" & rstQuelle!Region & \"'', ''\" & rstQuelle!PLZ  & \"'', ''\" _\r\n                     & rstQuelle!Land & \"'',''\" & rstQuelle!Telefon & \"'',''\" & rstQuelle!Telefax & \"'', \"  _\r\n                     & ISODatum(Now) & \")\", dbFailOnError\r\n                 intAnzahl = intAnzahl + 1\r\n             <span style=\"color:blue;\">End If<\/span>\r\n         <span style=\"color:blue;\">End If<\/span>\r\n         rstQuelle.Move<span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n     Datenkopieren_III = intAnzahl\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 4: AddNew und Update durch Execute und INSERT INTO ersetzen<\/span><\/b><\/p>\n<p>Eigentllich denkt man, dass dies schneller als die vorherige Variante sein sollte. Allerdings m&uuml;ssen wir in der Zeile, welche die Anweisung zusammenstellt und ausf&uuml;hrt, drei VBA-Funktionen unterbringen:<\/p>\n<ul>\n<li>Wir ben&ouml;tigen zwei Aufrufe der <b>Replace<\/b>-Funktion, um in den Feldern enthaltene Hochkommata durch doppelte Hochkommata zu ersetzen (<b>Replace(rstQuelle!Firma,&#8220;&#8220;&#8220;, &#8222;&#8220;&#8220;<\/b>&#8222;).<\/li>\n<li>Au&szlig;erdem m&uuml;ssen wir das mit der <b>Date<\/b>-Funktion gewonnene Datum mit der benutzerdefinierten Funktion <b>ISODatum <\/b>in einen SQL-vertr&auml;glichen Ausdruck umwandeln (zum Beispiel <b>#2013\/11\/05 00:00:00#<\/b>). <\/li>\n<\/ul>\n<p>Im Ergebnis f&uuml;hrt diese &auml;nderung im Vergleich zur vorherigen Version zu einer deutlich schlechteren Performance, die aber immer noch etwas besser als die der nicht optimierten Version ist. Aber: Es handelt sich nur um einen Zwischenschritt!<\/p>\n<p>Am SQL-Ausdruck k&ouml;nnen wir h&ouml;chstens eine kleine &auml;nderung vornehmen. In diesem Fall w&uuml;rden wir das per VBA erzeugte und formatierte Datum durch die reine <b>Now()<\/b>-Funktion ersetzen. Anscheinend wirkt sich diese Optimierung jedoch nicht entscheidend aus.<\/p>\n<p><b>Optimierung IV: Kriterium in Abfrage bringen<\/b><\/p>\n<p>Bislang pr&uuml;fen wir noch f&uuml;r jeden einzelnen Datensatz innerhalb der <b>Do While<\/b>-Schleife, ob das &uuml;ber die Gruppierung ermittelte Feld <b>NeuesteBestellung <\/b>kleiner als der Stichtag ist, oder f&uuml;llen es, wenn es leer ist, mit dem Wert <b>0<\/b> &#8211; was auf jeden Fall kleiner als der Stichtag ist (au&szlig;er, der Stichtag liegt vor dem <b>1.1.1900<\/b>).<\/p>\n<p>Diese Pr&uuml;fung sollten wir ebenfalls noch in die Abfrage verlegen. Aber wir hatten ja schon zuvor das Problem, dass wir das Ergebnis der Gruppierung noch auf den Wert <b>Null <\/b>h&auml;tten pr&uuml;fen und dieses gegebenenfalls durch den Wert <b>0 <\/b>h&auml;tten ersetzen m&uuml;ssen, um dieses mit dem Stichtag zu vergleichen.<\/p>\n<p>Es gibt jedoch eine recht elegante L&ouml;sung, wenn es darum geht, Kunden ohne Bestellung dennoch auszuw&auml;hlen: Wir vergleichen das Ergebnis der Gruppierung, also das Feld <b>NeuesteBestellung<\/b>, einfach mit dem Wert <b>Null<\/b>. Ist <b>NeuesteBestellung <\/b>leer, ist die Bedingung wahr und der Kunde kann archiviert werden. Wenn anderenfalls ein Datum f&uuml;r die neueste Bestellung vorliegt, verkn&uuml;pfen wir den Vergleich mit dem Stichtag einfach per <b>Oder <\/b>mit dem ersten Kriterium.<\/p>\n<p>Nun m&uuml;ssen wir nur noch den Vergleichswert, also den Stichtag, in die Abfrage bekommen. Dies erledigen wir mit einem Parameter namens <b>[Stichtag]<\/b>, den wir wie in Bild 6 in die Abfrage integrieren.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2013_06\/pic_911_006.png\" alt=\"Vergleich des neuesten Bestelldatums mit dem Stichtag\" width=\"700\" height=\"302,4586\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 6: Vergleich des neuesten Bestelldatums mit dem Stichtag<\/span><\/b><\/p>\n<p>An der Prozedur sind auch einige Anpassungen n&ouml;tig (s. Listing 5). Der Grund ist, dass wir der Abfrage mitteilen m&uuml;ssen, welchen Wert sie f&uuml;r den Parameter <b>[Stichtag] <\/b>einsetzen soll. Dazu ben&ouml;tigen wir zwei weitere Deklarationen, n&auml;mlich f&uuml;r das <b>QueryDef<\/b>-Objekt <b>qdf <\/b>und das <b>Parameter<\/b>-Objekt <b>prm<\/b>.<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>Datenkopieren_IV(datStichtag<span style=\"color:blue;\"> As Date<\/span>)<span style=\"color:blue;\"> As Integer<\/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>prm<span style=\"color:blue;\"> As <\/span>DAO.Parameter\r\n     <span style=\"color:blue;\">Dim <\/span>rstQuelle<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Dim <\/span>intAnzahl<span style=\"color:blue;\"> As Integer<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> qdf = db.QueryDefs(\"qryKundenNachBestelldatumMitStichtag\")\r\n     <span style=\"color:blue;\">Set<\/span> prm = qdf.Parameters(\"Stichtag\")\r\n     prm.Value = datStichtag\r\n     <span style=\"color:blue;\">Set<\/span> rstQuelle = qdf.OpenRecordset\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rstQuelle.EOF\r\n         <span style=\"color:blue;\">If <\/span>IsNull(DLookup(\"KundeID\", \"tblKunden_Archiv\", \"KundeID = \" & rstQuelle!KundeID))<span style=\"color:blue;\"> Then<\/span>\r\n             db.Execute \"INSERT INTO tblKunden_Archiv(KundeID, KundenCode, Firma, Kontaktperson, Strasse,... \", _\r\n                 dbFailOnError\r\n             intAnzahl = intAnzahl + 1\r\n         <span style=\"color:blue;\">End If<\/span>\r\n         rstQuelle.Move<span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n     Datenkopieren_IV = intAnzahl\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 5: Auslagern des Vergleichskriteriums in die Abfrage<\/span><\/b><\/p>\n<p>Die neue Version der Abfrage, <b>qryKundenNachBestelldatumMitStichtag<\/b>, referenzieren Sie nun zun&auml;chst mit der Variablen <b>qdf<\/b>.<\/p>\n<p>F&uuml;r diese legen Sie ein neues <b>Parameter<\/b>-Objekt auf Basis des Parameters <b>Stichtag <\/b>an und referenzieren es mit <b>prm<\/b>. Schlie&szlig;lich f&uuml;hren Sie die mit <b>qdf <\/b>referenzierte Abfrage mit der <b>OpenRecordset<\/b>-Methode aus und erhalten zuvor ein Recordset, das Sie dann wie zuvor durchlaufen k&ouml;nnen.<\/p>\n<p>Performancem&auml;&szlig;ig bringt uns dies eine Verbesserung um rund zehn Prozent gegen&uuml;ber der nicht optimierten Variante.<\/p>\n<p>Hier ist allerdings anzumerken, dass wir immer noch den hemmenden Einsatz der <b>INSERT INTO<\/b>-Aktionsabfrage mit uns herumtragen.<\/p>\n<p><b>Parameter als Datum deklarieren<\/b><\/p>\n<p>Beim Testen dieser Optimierung stellten wir fest, dass zun&auml;chst nicht alle 1.456 Datens&auml;tze ber&uuml;cksichtigt wurden, sondern ein paar weniger. Dies stellt sich als Problem mit der Parameter-Deklaration heraus. Wir hatten diesen zun&auml;chst nicht deklariert, weshalb er unser Testdatum (<b>5.11.2013<\/b>) als Text verarbeitet hat.<\/p>\n<p>Die Abfrage erkennt offenbar erst beim Durchf&uuml;hren des Vergleichs, dass der Parameter mit einem Datumsfeld verglichen werden soll, und konvertiert dieses entsprechend &#8211; allerdings wurde hier aus dem <b>5.11.2013 <\/b>der <b>11.5.2013<\/b>, was auch genau zu der falsch gelieferten Anzahl Datens&auml;tze passt. Und in der Tat lieferte die Abfrage des Parameter-Datentyps mit der folgenden Anweisung den Wert <b>8<\/b>, was der Konstanten <b>vbString <\/b>entspricht.<\/p>\n<pre><span style=\"color:blue;\">Debug.Print<\/span> prm.Type<\/pre>\n<p>Sie m&uuml;ssen also den Datentyp des Parameters im Abfrageentwurf auf <b>Datum\/Uhrzeit <\/b>einstellen (s. Bild 7).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2013_06\/pic_911_007.png\" alt=\"Einstellen des Datentyps des Parameters Stichtag\" width=\"400\" height=\"295,3959\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 7: Einstellen des Datentyps des Parameters Stichtag<\/span><\/b><\/p>\n<p><b>Optimierung V: Pr&uuml;fung auf bestehende KundeID-Werte in Abfrage verlagern<\/b><\/p>\n<p>Nun pr&uuml;fen wir innerhalb der Schleife noch mit einer <b>DLookup<\/b>-Abfrage, ob sich in der Tabelle <b>tblKunden_Archiv <\/b>bereits ein Datensatz befindet, der den gleichen Wert im Feld <b>KundeID <\/b>aufweist wie der Datensatz, der nun hinzugef&uuml;gt werden soll.<\/p>\n<p>Das Entfernen der <b>DMax<\/b>-Funktion weiter oben hat die Performance bereits stark optimiert, also versuchen wir auch die <b>DLookup<\/b>-Funktion aus der VBA-Funktion herauszubekommen.<\/p>\n<p>Im Grunde ist das kein Problem: Wir m&uuml;ssen nur eine entsprechende Unterabfrage als Kriterium der Abfrage hinzuf&uuml;gen. Diese sieht wie folgt aus:<\/p>\n<pre>Nicht In (SELECT KundeID FROM tblKunden_Archiv)<\/pre>\n<p>Die aktuelle Version der Abfrage, die diesmal <b>qryKundenNachBestelldatumMitVergleichAufKundeID <\/b>hei&szlig;t, sieht nun wie in Bild 8 aus.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2013_06\/pic_911_008.png\" alt=\"Entwurf der Abfrage qryKundenNachBestelldatumMitVergleichAufKundeID mit Unterabfrage\" width=\"700\" height=\"268,0112\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 8: Entwurf der Abfrage qryKundenNachBestelldatumMitVergleichAufKundeID mit Unterabfrage<\/span><\/b><\/p>\n<p>Auch hier ist wieder eine kleine Anpassung des VBA-Codes n&ouml;tig. Dies &auml;u&szlig;ert sich darin, dass wir die letzte <b>If&#8230;Then<\/b>-Bedingung aus der Schleife entfernen k&ouml;nnen.<\/p>\n<p>Diese enth&auml;lt nun nur noch die Anweisung zum Schreiben des aktuellen Datensatzes in die Tabelle <b>tblKunden_Archiv<\/b> und die Erh&ouml;hung des Z&auml;hlers <b>intAnzahl<\/b> (s. Listing 6).<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>Datenkopieren_V(datStichtag<span style=\"color:blue;\"> As Date<\/span>)<span style=\"color:blue;\"> As Integer<\/span>\r\n     ...\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rstQuelle.EOF\r\n         db.Execute \"INSERT INTO tblKunden_Archiv(KundeID, KundenCode, ...\r\n         intAnzahl = intAnzahl + 1\r\n         rstQuelle.Move<span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n     Datenkopieren_V = intAnzahl\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 6: Auch die letzte If&#8230;Then-Bedingung haben wir noch aus der Abfrage entfernt.<\/span><\/b><\/p>\n<p>Und nun wird es interessant: Wie wirkt sich diese Optimierung auf die Performance aus Das Ergebnis ist hervorragend: Die neue Version ben&ouml;tigt 70% weniger Zeit als die nicht optimierte Variante.<\/p>\n<p><b>Optimierung VI: Do While entfernen<\/b><\/p>\n<p>Nun gehen wir den letzten Performancefresser an: die <b>Do While<\/b>-Schleife mit dem x-fachen Aufruf der <b>INSERT INTO<\/b>-Abfrage. Wir haben mittlerweile alle weiteren Pr&uuml;fungen und Informationen in die Abfrage &uuml;bertragen, welche die Daten zum Einf&uuml;gen in die Tabelle <b>tblKunden_Archiv <\/b>liefert. Was sollte uns noch davon abhalten, aus dieser Auswahlabfrage einfach eine Aktionsabfrage zu machen, die ihre Daten einfach direkt in die Zielabfrage schreibt &#8211; ohne Umweg &uuml;ber eine <b>Do While<\/b>-Schleife und eine per VBA generierte <b>INSERT INTO<\/b>-Anweisung Sie ahnen es: Davon h&auml;lt uns nichts ab!<\/p>\n<p>Also, ans Werk: Wir erstellen eine neue Abfrage auf Basis der Abfrage <b>qryKundenNachBestelldatumMitVergleichAufKundeID<\/b>, die wir <b>qryKundenNachBestelldatumMitInsertInto <\/b>nennen.<\/p>\n<p>&ouml;ffnen Sie diese Abfrage im Entwurf und w&auml;hlen Sie den Kontextmen&uuml;-Eintrag <b>Abfragetyp|Anf&uuml;geabfrage&#8230;<\/b> aus. Legen Sie die Tabelle <b>tblKunden_Archiv <\/b>als Zieltabelle fest. Access f&uuml;gt nun eine weitere Zeile namens <b>Anf&uuml;gen an: <\/b>zum Entwurfsraster der Abfrage hinzu. Die meisten Zielfelder werden dort automatisch eingetragen. Allein das Feld <b>ArchiviertAm <\/b>m&uuml;ssen Sie noch vollst&auml;ndig hinzuf&uuml;gen, und zwar mit folgendem Ausdruck:<\/p>\n<pre>ArchiviertAm: Jetzt()<\/pre>\n<p>Dies ermittelt dynamisch das aktuelle Datum und die aktuelle Uhrzeit und liefert diese unter dem Feldnamen <b>ArchiviertAm<\/b> aus. W&auml;hlen Sie auch f&uuml;r dieses Feld ein Zielfeld aus, und zwar <b>ArchiviertAm<\/b>. Die Abfrage sieht nun etwa wie in Bild 9 aus.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2013_06\/pic_911_009.png\" alt=\"Die endg&uuml;ltige Fassung der Abfrage wird als INSERT INTO-Aktionsabfrage implementiert.\" width=\"700\" height=\"261,2275\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 9: Die endg&uuml;ltige Fassung der Abfrage wird als INSERT INTO-Aktionsabfrage implementiert.<\/span><\/b><\/p>\n<p>Den Code k&ouml;nnen Sie nat&uuml;rlich weiter entschlacken. Die <b>Do While<\/b>-Schleife ben&ouml;tigen wir ebenso wenig wie die <b>Execute<\/b>-Methode zum Ausf&uuml;hren der <b>INSERT INTO<\/b>-Anweisungen.<\/p>\n<p>Diese weichen dem schlichten Aufruf der Methode <b>Execute <\/b>des <b>QueryDef<\/b>-Objekts <b>qdf<\/b>. Auch die Z&auml;hlervariable <b>intAnzahl <\/b>ist nicht mehr vorhanden &#8211; die Anzahl der betroffenen Datens&auml;tze liefert nunmehr direkt die Eigenschaft <b>RecordsAffected <\/b>des <b>QueryDef<\/b>-Objekts (s. Listing 7).<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>Datenkopieren_VI(datStichtag<span style=\"color:blue;\"> As Date<\/span>)<span style=\"color:blue;\"> As Integer<\/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>prm<span style=\"color:blue;\"> As <\/span>DAO.Parameter\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> qdf = db.QueryDefs(\"qryKundenNachBestelldatumMitInsertInto\")\r\n     <span style=\"color:blue;\">Set<\/span> prm = qdf.Parameters(\"Stichtag\")\r\n     prm.Value = datStichtag\r\n     qdf.Execute\r\n     Datenkopieren_VI = qdf.RecordsAffected\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 7: Die zusammengeschrumpfte Version der Funktion Datenkopieren<\/span><\/b><\/p>\n<p><b>Performance<\/b><\/p>\n<p>Abschlie&szlig;end interessiert uns nat&uuml;rlich noch die Performance dieser Variante im Vergleich zur Ausgangsversion. Und siehe da: Die Performance ist um fast 99% schneller als zu Beginn!<\/p>\n<p>Damit haben Sie in jedem Fall handfeste Gr&uuml;nde, L&ouml;sungen zum &auml;ndern von Daten auf Basis von Recordsets und Schleifen auf reine SQL-Aktionsabfragen umzustellen. Bild 10 zeigt die Ergebnisse einer kompletten Reihe (ohne Wiederholungen) in einem Formular. Au&szlig;erdem ist auch zu erkennen, dass der Programmieraufwand auf VBA-Seite erheblich verringert wird. Auf der anderen Seite steigt nat&uuml;rlich der Aufwand f&uuml;r die Entwicklung einer geeigneten Aktionsabfrage.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2013_06\/pic_911_010.png\" alt=\"Messergebnisse bei der Verbesserung der Performance\" width=\"575\" height=\"558,1545\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 10: Messergebnisse bei der Verbesserung der Performance<\/span><\/b><\/p>\n<h3>Downloads zu diesem Beitrag<\/h3>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>AktionsabfragenStattSchleifen.mdb<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/{A0C81260-ADFE-42F5-8DDE-5877D2C9BC53}\/aiu_911.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>&Auml;nderungen an den Daten in den Tabellen einer Datenbank kann man auf verschiedenste Arten durchf&uuml;hren &#8211; zum Beispiel &uuml;ber die Benutzeroberfl&auml;che. Gelegentlich werden Sie jedoch auch Daten per VBA &auml;ndern wollen. In diesem Falle gibt es eine ganze Reihe von Varianten, die normalerweise mit den Methoden der DAO-Bibliotheken abgebildet werden: Das &Ouml;ffnen eines Recordsets und &Auml;ndern der enthaltenen Daten mit AddNew\/Edit und Update, das Zusammenstellen einer SQL-Aktionsabfrage, die dann mit der Execute-Methode des Database-Objekts ausgef&uuml;hrt wird, oder der Einsatz gespeicherter Aktionsabfragen, die man mit der Execute-Methode des QueryDef-Objekts startet. Wir schauen uns die verschiedenen Varianten an und optimieren diese.<\/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":[662013,66062013,44000029],"tags":[],"class_list":["post-55000911","post","type-post","status-publish","format-standard","hentry","category-662013","category-66062013","category-Abfragetechnik_und_SQL"],"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>Aktionsabfragen statt Schleifen - 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\/Aktionsabfragen_statt_Schleifen\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Aktionsabfragen statt Schleifen\" \/>\n<meta property=\"og:description\" content=\"&Auml;nderungen an den Daten in den Tabellen einer Datenbank kann man auf verschiedenste Arten durchf&uuml;hren - zum Beispiel &uuml;ber die Benutzeroberfl&auml;che. Gelegentlich werden Sie jedoch auch Daten per VBA &auml;ndern wollen. In diesem Falle gibt es eine ganze Reihe von Varianten, die normalerweise mit den Methoden der DAO-Bibliotheken abgebildet werden: Das &Ouml;ffnen eines Recordsets und &Auml;ndern der enthaltenen Daten mit AddNew\/Edit und Update, das Zusammenstellen einer SQL-Aktionsabfrage, die dann mit der Execute-Methode des Database-Objekts ausgef&uuml;hrt wird, oder der Einsatz gespeicherter Aktionsabfragen, die man mit der Execute-Methode des QueryDef-Objekts startet. Wir schauen uns die verschiedenen Varianten an und optimieren diese.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/access-im-unternehmen.de\/Aktionsabfragen_statt_Schleifen\/\" \/>\n<meta property=\"og:site_name\" content=\"Access im Unternehmen\" \/>\n<meta property=\"article:published_time\" content=\"2020-05-22T21:31:36+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/vg05.met.vgwort.de\/na\/94285e3006074741932a61ef6eb8550d\" \/>\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=\"20\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Aktionsabfragen_statt_Schleifen\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Aktionsabfragen_statt_Schleifen\\\/\"},\"author\":{\"name\":\"Andr\u00e9 Minhorst\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\"},\"headline\":\"Aktionsabfragen statt Schleifen\",\"datePublished\":\"2020-05-22T21:31:36+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Aktionsabfragen_statt_Schleifen\\\/\"},\"wordCount\":3404,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Aktionsabfragen_statt_Schleifen\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg05.met.vgwort.de\\\/na\\\/94285e3006074741932a61ef6eb8550d\",\"articleSection\":[\"2013\",\"6\\\/2013\",\"Abfragetechnik und SQL\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Aktionsabfragen_statt_Schleifen\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Aktionsabfragen_statt_Schleifen\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Aktionsabfragen_statt_Schleifen\\\/\",\"name\":\"Aktionsabfragen statt Schleifen - Access im Unternehmen\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Aktionsabfragen_statt_Schleifen\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Aktionsabfragen_statt_Schleifen\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg05.met.vgwort.de\\\/na\\\/94285e3006074741932a61ef6eb8550d\",\"datePublished\":\"2020-05-22T21:31:36+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Aktionsabfragen_statt_Schleifen\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Aktionsabfragen_statt_Schleifen\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Aktionsabfragen_statt_Schleifen\\\/#primaryimage\",\"url\":\"http:\\\/\\\/vg05.met.vgwort.de\\\/na\\\/94285e3006074741932a61ef6eb8550d\",\"contentUrl\":\"http:\\\/\\\/vg05.met.vgwort.de\\\/na\\\/94285e3006074741932a61ef6eb8550d\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Aktionsabfragen_statt_Schleifen\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Aktionsabfragen statt Schleifen\"}]},{\"@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":"Aktionsabfragen statt Schleifen - 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\/Aktionsabfragen_statt_Schleifen\/","og_locale":"de_DE","og_type":"article","og_title":"Aktionsabfragen statt Schleifen","og_description":"&Auml;nderungen an den Daten in den Tabellen einer Datenbank kann man auf verschiedenste Arten durchf&uuml;hren - zum Beispiel &uuml;ber die Benutzeroberfl&auml;che. Gelegentlich werden Sie jedoch auch Daten per VBA &auml;ndern wollen. In diesem Falle gibt es eine ganze Reihe von Varianten, die normalerweise mit den Methoden der DAO-Bibliotheken abgebildet werden: Das &Ouml;ffnen eines Recordsets und &Auml;ndern der enthaltenen Daten mit AddNew\/Edit und Update, das Zusammenstellen einer SQL-Aktionsabfrage, die dann mit der Execute-Methode des Database-Objekts ausgef&uuml;hrt wird, oder der Einsatz gespeicherter Aktionsabfragen, die man mit der Execute-Methode des QueryDef-Objekts startet. Wir schauen uns die verschiedenen Varianten an und optimieren diese.","og_url":"https:\/\/access-im-unternehmen.de\/Aktionsabfragen_statt_Schleifen\/","og_site_name":"Access im Unternehmen","article_published_time":"2020-05-22T21:31:36+00:00","og_image":[{"url":"http:\/\/vg05.met.vgwort.de\/na\/94285e3006074741932a61ef6eb8550d","type":"","width":"","height":""}],"author":"Andr\u00e9 Minhorst","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Andr\u00e9 Minhorst","Gesch\u00e4tzte Lesezeit":"20\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/access-im-unternehmen.de\/Aktionsabfragen_statt_Schleifen\/#article","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/Aktionsabfragen_statt_Schleifen\/"},"author":{"name":"Andr\u00e9 Minhorst","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f"},"headline":"Aktionsabfragen statt Schleifen","datePublished":"2020-05-22T21:31:36+00:00","mainEntityOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Aktionsabfragen_statt_Schleifen\/"},"wordCount":3404,"commentCount":0,"publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Aktionsabfragen_statt_Schleifen\/#primaryimage"},"thumbnailUrl":"http:\/\/vg05.met.vgwort.de\/na\/94285e3006074741932a61ef6eb8550d","articleSection":["2013","6\/2013","Abfragetechnik und SQL"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/access-im-unternehmen.de\/Aktionsabfragen_statt_Schleifen\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/access-im-unternehmen.de\/Aktionsabfragen_statt_Schleifen\/","url":"https:\/\/access-im-unternehmen.de\/Aktionsabfragen_statt_Schleifen\/","name":"Aktionsabfragen statt Schleifen - Access im Unternehmen","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Aktionsabfragen_statt_Schleifen\/#primaryimage"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Aktionsabfragen_statt_Schleifen\/#primaryimage"},"thumbnailUrl":"http:\/\/vg05.met.vgwort.de\/na\/94285e3006074741932a61ef6eb8550d","datePublished":"2020-05-22T21:31:36+00:00","breadcrumb":{"@id":"https:\/\/access-im-unternehmen.de\/Aktionsabfragen_statt_Schleifen\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/access-im-unternehmen.de\/Aktionsabfragen_statt_Schleifen\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/Aktionsabfragen_statt_Schleifen\/#primaryimage","url":"http:\/\/vg05.met.vgwort.de\/na\/94285e3006074741932a61ef6eb8550d","contentUrl":"http:\/\/vg05.met.vgwort.de\/na\/94285e3006074741932a61ef6eb8550d"},{"@type":"BreadcrumbList","@id":"https:\/\/access-im-unternehmen.de\/Aktionsabfragen_statt_Schleifen\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/access-im-unternehmen.de\/"},{"@type":"ListItem","position":2,"name":"Aktionsabfragen statt Schleifen"}]},{"@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\/55000911","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=55000911"}],"version-history":[{"count":0,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55000911\/revisions"}],"wp:attachment":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/media?parent=55000911"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/categories?post=55000911"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/tags?post=55000911"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}