{"id":55001208,"date":"2019-10-01T00:00:00","date_gmt":"2020-05-29T12:07:20","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=1208"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"Von_Version_zu_Version","status":"publish","type":"post","link":"https:\/\/access-im-unternehmen.de\/Von_Version_zu_Version\/","title":{"rendered":"Von Version zu Version"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg06.met.vgwort.de\/na\/277900bba37b4dc3bb1008993dfa25d3\" width=\"1\" height=\"1\" alt=\"\"><\/p>\n<p><b>Neulich wollte ich meine Shopsoftware aktualisieren. Allerdings ging dies nicht mit den vom Hersteller daf&uuml;r bereitgestellten Plug-In &#8211; zumindest nicht mit allen Daten, die ich von der alten in die neue Version &uuml;berf&uuml;hren wollte. Also musste ich manuell ermitteln, welche Daten der alten Version ben&ouml;tigt werden, damit die neue Version l&auml;uft. Damit erhielt ich recht schnell eine lauff&auml;hige Version des neuen Systems. Allerdings kam mir dann etwas dazwischen, wodurch zum alten, noch aktiven System wieder neue Kunden und Bestellungen hinzukamen &#8211; die Arbeit war also umsonst. Um das &Uuml;bertragen der Daten beim n&auml;chsten Mal einfacher zu gestalten, wollte ich nun das alte und das neue Datenmodell nun automatisch analysieren und die SQL-Befehle erstellen lassen, um diese danach ebenfalls automatisch ausf&uuml;hren zu k&ouml;nnen. Wie das gelingt, zeigt der vorliegende Beitrag.<\/b><\/p>\n<p>Bei der Shopsoftware handelt es sich um ein Produkt namens <b>Shopware<\/b>. Es verwendet eine MySQL-Datenbank.<\/p>\n<p>Die hier vorgestellten Techniken lassen sich jedoch auch auf andere Systeme &uuml;bertragen &#8211; und auch auf andere Anwendungen.<\/p>\n<p>Auch MySQL als Datenbanksystem kann durch andere Systeme ersetzt werden &#8211; Sie m&uuml;ssen dann schlicht den Treiber in der Verbindungszeichenfolge austauschen und gegebenenfalls ein paar Abfragen auf spezifische Eigenarten des jeweiligen Dialekts anpassen. Voraussetzung f&uuml;r die Nutzung der hier vorgestellten Technik ist der Zugriff auf die Datenbank. Ob dies m&ouml;glich ist, m&uuml;ssen Sie mit dem jeweiligen Provider kl&auml;ren.<\/p>\n<p><b>Zugriff per ADODB<\/b><\/p>\n<p>Damit wir per ODBC auf die Daten der beiden zu vergleichenden MySQL-Datenbanken zugreifen k&ouml;nnen, f&uuml;gen wir der Datenbank einen Verweis auf die Bibliothek <b>Microsoft ActiveX Data Objects 2.8 Library <\/b>zum VBA-Projekt einer neuen Access-Datenbank hinzu (siehe Bild 1).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2019_05\/pic_1208_001.png\" alt=\"Verweis auf die ADODB-Bibliothek\" width=\"424,7115\" height=\"334,8159\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Verweis auf die ADODB-Bibliothek<\/span><\/b><\/p>\n<p><b>Tabellen abgleichen<\/b><\/p>\n<p>Als Erstes wollen wir die Tabellen der beiden Datenbanken abgleichen, also pr&uuml;fen, ob es in der ersten Datenbank Tabellen gibt, die nicht in der zweiten Datenbank vorkommen und umgekehrt.<\/p>\n<p>Dazu legen wir zwei Konstanten mit den Verbindungszeichenfolgen der beiden Datenbanken in einem neuen Modul namens <b>mdlADODB <\/b>an:<\/p>\n<pre>Const cStrVerbindungszeichenfolgeAlt<span style=\"color:blue;\"> As String<\/span> = \"DRIVER={MySQL ODBC 5.3 ANSI Driver};SERVER=xxx.xxx.xxx.xxx;DATABASE=&lt;Datenbankname&gt;;UID=&lt;Benutzername&gt;;PWD=&lt;Kennwort&gt;\"\r\nConst cStrVerbindungszeichenfolgeNeu<span style=\"color:blue;\"> As String<\/span> = \"DRIVER={MySQL ODBC 5.3 ANSI Driver};SERVER=xxx.xxx.xxx.xxx;DATABASE=&lt;Datenbankname&gt;;UID=&lt;Benutzername&gt;;PWD=&lt;Kennwort&gt;\"<\/pre>\n<p>Die Funktion <b>GetConnection <\/b>erwartet die Angabe der zu verwendenden Verbindungszeichenfolge als Parameter. Sie erstellt ein neues Objekt des Typs <b>ADODB.Connection <\/b>und &ouml;ffnet diese unter Angabe der Verbindungszeichenfolge (siehe Listing 1).<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>GetConnection(strConnection<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As <\/span>ADODB.Connection\r\n     <span style=\"color:blue;\">Dim <\/span>objConnection<span style=\"color:blue;\"> As <\/span>ADODB.Connection\r\n     <span style=\"color:blue;\">Set<\/span> objConnection = <span style=\"color:blue;\">New<\/span> ADODB.Connection\r\n     objConnection.Open strConnection\r\n     <span style=\"color:blue;\">Set<\/span> GetConnection = objConnection\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Die Funktion GetConnection<\/span><\/b><\/p>\n<p>Die Funktion <b>GetRecordset <\/b>erwartet ein <b>ADODB.Connection<\/b>-Objekt, das wir zuvor beispielsweise mit der Funktion <b>GetConnection <\/b>ermitteln, sowie eine SQL-Anweisung mit der zu verwendenden Abfrage (siehe Listing 2). Die Funktion erstellt ein neues <b>ADODB.Recordset<\/b>-Objekt und stellt seine Eigenschaften ein, darunter auch die SQL-Abfrage f&uuml;r die Eigenschaft <b>Source<\/b>. Das Ergebnis wird dann als <b>ADODB.Recordset<\/b>-Objekt zur&uuml;ckgegeben.<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>GetRecordset(objConnection<span style=\"color:blue;\"> As <\/span>ADODB.Connection, strSQL<span style=\"color:blue;\"> As String<\/span>) _\r\n        <span style=\"color:blue;\"> As <\/span>ADODB.Recordset\r\n     <span style=\"color:blue;\">Dim <\/span>rst<span style=\"color:blue;\"> As <\/span>ADODB.Recordset\r\n     <span style=\"color:blue;\">Set<\/span> rst = <span style=\"color:blue;\">New<\/span> ADODB.Recordset\r\n     <span style=\"color:blue;\">With<\/span> rst\r\n         .ActiveConnection = objConnection\r\n         .CursorLocation = adUseClient\r\n         .CursorType = adOpenDynamic\r\n         .LockType = adLockBatchOptimistic\r\n         .Source = strSQL\r\n         .Open , , , , adCmdText\r\n         <span style=\"color:blue;\">Set<\/span> GetRecordset = rst\r\n     End <span style=\"color:blue;\">With<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 2: Die Funktion GetRecordset<\/span><\/b><\/p>\n<p>Danach nutzen wir diese beiden Funktionen in einer neuen Prozedur namens <b>TabellenAbgleichen<\/b> (siehe Listing 3). Diese Prozedur verwendet zwei <b>ADODB.Recordset<\/b>-Objekte namens <b>rstAlt <\/b>und <b>rstNeu<\/b>, um die Namen der Tabellen der alten und der neuen Version der Datenbank zu speichern. Diese f&uuml;llen wir jeweils mit einem Aufruf der Funktion <b>GetRecordset<\/b>, wobei wir einmal die Verbindungszeichenfolge aus <b>cStrVerbindungszeichenfolgeAlt <\/b>und einmal die aus <b>cStrVerbindungszeichenfolgeNeu <\/b>&uuml;bergeben. In beiden F&auml;llen geben wir als SQL-Anweisung keine einfache <b>SELECT<\/b>-Anweisung an, sondern die Anweisung <b>Show Tables<\/b>. Dies ist eine MySQL-spezifische Anweisung, die alle Tabellen einer Datenbank liefert. Danach sortieren wir die Inhalte der beiden Recordsets <b>rstAlt <\/b>und <b>rstNeu <\/b>aufsteigend nach dem Inhalt des einzigen Feldes.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>TabellenAbgleichen()\r\n     <span style=\"color:blue;\">Dim <\/span>rstAlt<span style=\"color:blue;\"> As <\/span>ADODB.Recordset\r\n     <span style=\"color:blue;\">Dim <\/span>rstNeu<span style=\"color:blue;\"> As <\/span>ADODB.Recordset\r\n     <span style=\"color:blue;\">Dim <\/span>strKriterium<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>fld<span style=\"color:blue;\"> As <\/span>ADODB.Field\r\n     <span style=\"color:blue;\">Set<\/span> rstAlt = GetRecordset(GetConnection(cStrVerbindungszeichenfolgeAlt), \"SHOW TABLES\")\r\n     <span style=\"color:blue;\">Set<\/span> rstNeu = GetRecordset(GetConnection(cStrVerbindungszeichenfolgeNeu), \"SHOW TABLES\")\r\n     rstAlt.Sort = rstAlt.Fields(0).Name & \" ASC\"\r\n     rstNeu.Sort = rstNeu.Fields(0).Name & \" ASC\"\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rstAlt.EOF\r\n         <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> rstNeu.EOF<span style=\"color:blue;\"> Then<\/span>\r\n             <span style=\"color:blue;\">If <\/span>rstAlt.Fields(0) = rstNeu.Fields(0)<span style=\"color:blue;\"> Then<\/span>\r\n                 <span style=\"color:blue;\">Debug.Print<\/span> rstAlt.Fields(0), rstNeu.Fields(0)\r\n                 rstAlt.Move<span style=\"color:blue;\">Next<\/span>\r\n                 rstNeu.Move<span style=\"color:blue;\">Next<\/span>\r\n             <span style=\"color:blue;\">Else<\/span>If rstAlt.Fields(0) &gt; rstNeu.Fields(0) Then\r\n                 <span style=\"color:blue;\">Debug.Print<\/span> \"----\", rstNeu.Fields(0)\r\n                 rstNeu.Move<span style=\"color:blue;\">Next<\/span>\r\n             <span style=\"color:blue;\">Else<\/span>If rstAlt.Fields(0) &lt; rstNeu.Fields(0) Then\r\n                 <span style=\"color:blue;\">Debug.Print<\/span> rstAlt.Fields(0), \"----\"\r\n                 rstAlt.Move<span style=\"color:blue;\">Next<\/span>\r\n             <span style=\"color:blue;\">End If<\/span>\r\n         <span style=\"color:blue;\">Else<\/span>\r\n             <span style=\"color:blue;\">Debug.Print<\/span> rstAlt.Fields(0), \"----\"\r\n             rstAlt.Move<span style=\"color:blue;\">Next<\/span>\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rstNeu.EOF\r\n         <span style=\"color:blue;\">Debug.Print<\/span> \"----\", rstNeu.Fields(0)\r\n         rstNeu.Move<span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 3: Die Prozedur TabellenAbgleichen<\/span><\/b><\/p>\n<p>Schlie&szlig;lich durchlaufen wir das Recordset <b>rstAlt <\/b>in einer <b>Do While<\/b>-Schleife, bis die Datensatzmarkierung des auf <b>EOF <\/b>steht. In dieser Schleife durchlaufen wir auf bestimmte Weise beide Recordsets, wobei gegebenenfalls noch ein oder mehrere Datens&auml;tze im Recordset <b>rstNeu <\/b>&uuml;brig bleiben.<\/p>\n<p>Dabei pr&uuml;fen wir zun&auml;chst, ob <b>rstNeu.EOF <\/b>wahr ist. Das ist zu Beginn in der Regel noch nicht der Fall &#8211; au&szlig;er, die neue Datenbank enth&auml;lt gar keine Tabellen. Anderenfalls pr&uuml;fen wir, ob der aktuelle Datensatz von <b>rstAlt <\/b>und <b>rstNeu <\/b>den gleichen Wert aufweisen, was darauf hindeutet, dass die Tabelle in beiden Datenbanken vorhanden ist. In diesem Fall geben wir im Direktbereich den Namen der Tabelle zwei Mal aus und verschieben die Datensatzzeiger beider Recordsets mit <b>MoveNext<\/b> zur n&auml;chsten Position. Ist <b>rstAlt.Fields(0) > rstNeu.Fields(0)<\/b>, dann stimmen die aktuellen Tabellennamen nicht &uuml;berein und der Tabellenname aus <b>rstNeu<\/b> liegt im Alphabet vor dem aus <b>rstAlt<\/b>. In diesem Fall geben wir eine Zeichenkette aus vier Minuszeichen (<b>&#8212;-<\/b>) aus und den Namen der Tabelle aus <b>rstNeu<\/b>. Au&szlig;erdem verschieben wir den Datensatzzeiger von <b>rstNeu <\/b>um eine Position weiter.<\/p>\n<p>Das hei&szlig;t, wir bleiben bei dem Tabellennamen von <b>rstAlt <\/b>und wechseln zum n&auml;chsten Eintrag von <b>rstNeu<\/b>. Die n&auml;chst <b>ElseIf<\/b>-Bedingung behandelt den umgekehrten Fall, n&auml;mlich dass der Eintrag aus <b>rstAlt <\/b>im Alphabet vor dem Eintrag aus <b>rstNeu <\/b>liegt. Dann geben wir den aus <b>rstAlt <\/b>aus und vier Minuszeichen statt des Eintrags aus <b>rstNeu <\/b>und verschieben den Datensatzzeiger in <b>rstAlt <\/b>um eine Position weiter nach hinten.<\/p>\n<p>Wenn also etwa in beiden Datenbanken <b>tblA <\/b>und <b>tblB <\/b>vorhanden sind, werden diese so ausgegeben:<\/p>\n<pre>tblA tblA\r\ntblB tblB<\/pre>\n<p>Wenn in <b>tblA <\/b>nur in <b>rstNeu <\/b>vorhanden ist, erhalten wir dieses Ergebnis:<\/p>\n<pre>---- tblA\r\ntblB tblB<\/pre>\n<p>Ist <b>tblA <\/b>in beiden Datenbanken vorhanden und <b>tblB <\/b>nur in der alten, sieht das Ergebnis so aus:<\/p>\n<pre>tblA tblA\r\ntblB ----<\/pre>\n<p>Nun gibt es drei F&auml;lle f&uuml;r das Ende des Durchlaufens der Datens&auml;tze mit den Tabellenamen in der ersten <b>Do While<\/b>-Schleife:<\/p>\n<ul>\n<li>Die letzte Tabelle ist in beiden Datenbanken enthalten. Dann wird die erste <b>Do While<\/b>-Schleife mit dem <b>If<\/b>-Teil der inneren <b>If&#8230;Then<\/b>-Bedingung verlassen, weil <b>NOT rstAlt.EOF <\/b>danach nicht mehr erf&uuml;llt ist. <b>rstNeu.EOF <\/b>ist dann auch <b>True<\/b>.<\/li>\n<li>Die letzte Tabelle beziehungsweise die letzten Tabellen sind nur in <b>rstAlt <\/b>enthalten. Dann ist die &auml;u&szlig;ere <b>If&#8230;Then<\/b>-Schleife <b>False <\/b>und der <b>Else<\/b>-Teil wird ausgef&uuml;hrt, wo der Datensatzzeiger in <b>rstAlt <\/b>solange weitergeschoben wird, bis die <b>Do While<\/b>-Bedingung <b>rstAlt.EOF <\/b>erf&uuml;llt ist.<\/li>\n<li>Die letzte Tabelle beziehungsweise die letzten Tabellen sind nur in <b>rstNeu <\/b>enthalten. Dann wird die erste <b>Do While<\/b>-Schleife verlassen, nachdem der Datensatzzeiger f&uuml;r <b>rstAlt <\/b>auf <b>EOF <\/b>verschoben wird.<\/li>\n<\/ul>\n<p>In allen F&auml;llen wird die Bedingung der zweiten <b>Do While<\/b>-Schleife noch einmal gepr&uuml;ft, n&auml;mlich <b>Not rstNeu.EOF<\/b>. Sollten also nach dem Durchlaufen von <b>rstAlt <\/b>noch Datens&auml;tze in <b>rstNeu <\/b>vorhanden sein, durchl&auml;uft die Prozedur diese in der zweiten <b>Do While<\/b>-Schleife. Dort gibt sie f&uuml;r die &uuml;brigen Tabellen der neuen Version der Datenbank die Tabellennamen aus, also etwa so:<\/p>\n<pre>...\r\n---- tblY\r\n---- tblZ<\/pre>\n<p><b>Interpretation des Ergebnisses<\/b><\/p>\n<p>Damit erhalten wir nun eine Gegen&uuml;berstellung der Tabellen der alten und der neuen Version der Datenbank, die etwa so aussehen k&ouml;nnte:<\/p>\n<pre>tblA tblA\r\ntblB tblB\r\n---- tblC\r\ntblD ----\r\n...\r\ntblY tblY\r\ntblZ tblZ<\/pre>\n<p>Die Tabelle <b>tblC <\/b>kommt nicht in der alten Datenbank vor, aber in der neuen Version. <b>tblD <\/b>hingegen kommt nur in der alten, aber nicht in der neuen Version vor. Alle anderen Tabellen sind in beiden Versionen der Datenbank vorhanden.<\/p>\n<p>Die Interpretation k&ouml;nnte wie folgt lauten:<\/p>\n<ul>\n<li>Eine Tabelle kommt in der alten Version vor, aber nicht mehr in der neuen und umgekehrt: Die Tabelle wurde schlicht unbenannt, daher wird sie nicht mehr als gleiche Tabelle erkannt.<\/li>\n<li>Eine Tabelle kommt in der alten Version vor, aber nicht mehr in der neuen und eine Umbenennung ist ausgeschlossen: Die Funktion, f&uuml;r die die Daten der Tabelle ben&ouml;tigt wurden, ist gegebenenfalls weggefallen. Oder, in meinem Beispiel mit der neuen Version des Shops geschehen: Die alte Version des Shops enthielt Plugins, die der neuen Version noch nicht hinzugef&uuml;gt wurden, daher waren die entsprechenden Tabellen dort auch noch nicht enthalten. Sie k&ouml;nnen also so gegebenenfalls erkennen, dass Sie noch Plugins hinzuf&uuml;gen m&uuml;ssen, damit der neue Shop wie der alte funktioniert.<\/li>\n<li>Eine Tabelle kommt in der neuen Version der Datenbank vor, aber nicht in der alten, und eine Umbenennung ist ausgeschlossen: Dann handelt es sich wohl um eine Tabelle, die im Rahmen einer neuen Funktion der neuen Version der Software ben&ouml;tigt wird.<\/li>\n<\/ul>\n<p><!--30percent--><\/p>\n<p><b>Felder vergleichen<\/b><\/p>\n<p>Wenn wir nun die Daten aus der alten Version der Datenbank in die neue Version &uuml;bertragen wollen, k&ouml;nnten wir theoretisch f&uuml;r jede Tabelle, die in der alten und in der neuen Version vorhanden ist, folgende Anweisung schreiben (vorausgesetzt, die Datenbanken befinden sich auf dem gleichen MySQL-Server):<\/p>\n<pre>INSERT INTO &lt;Neue Datenbank&gt;.&lt;Tabellenname&gt; SELECT * FROM &lt;Alte Datenbank&gt;.&lt;Tabellenname&gt;<\/pre>\n<p>Das klappt aber auch nur, wenn die Struktur der Tabelle in der alten und der neuen Version der Datenbank identisch ist, also wenn die Tabellen die gleichen Felder enthalten. Anderenfalls gibt es eine Fehlermeldung.<\/p>\n<p>In diesem Fall m&uuml;ssen wir untersuchen, welche der Felder der Tabelle der alten Version der Datenbank in der Tabelle der neuen Version der Datenbank vorkommen und diese statt des Platzhalters Sternchen (<b>*<\/b>) in die <b>SELECT&#8230;INTO<\/b>-Anweisung einf&uuml;gen.<\/p>\n<p><b>Gemeinsame Felder ermitteln<\/b><\/p>\n<p>Um diese gemeinsamen Felder zu ermitteln, f&uuml;gen wir dem <b>If<\/b>-Zweig der inneren <b>If&#8230;Then<\/b>-Bedingung den Aufruf der Funktion <b>FelderAbgleichen <\/b>mit dem Namen der Tabelle als Parameter hinzu. Das Ergebnis der Aufrufe dieser Funktion f&uuml;gen wir in der Variablen <b>strSQLInsert <\/b>zusammen. Auf die gleiche Weise f&uuml;gen wir in der Variablen <b>strSQLDelete <\/b>jeweils eine <b>DELETE<\/b>-Anweisung f&uuml;r die betroffenen Tabellen der neuen Version der Datenbank zusammen:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>TabellenAbgleichen()\r\n     ...\r\n     <span style=\"color:blue;\">Dim <\/span>strSQLDelete<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strSQLInsert<span style=\"color:blue;\"> As String<\/span>\r\n     ...\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rstAlt.EOF\r\n         <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> rstNeu.EOF<span style=\"color:blue;\"> Then<\/span>\r\n             <span style=\"color:blue;\">If <\/span>rstAlt.Fields(0) = rstNeu.Fields(0)<span style=\"color:blue;\"> Then<\/span>\r\n                 <span style=\"color:blue;\">Debug.Print<\/span> rstAlt.Fields(0),  rstNeu.Fields(0)\r\n                 strSQLDelete = strSQLDelete _\r\n                     & \"DELETE FROM \" & cStrNeueDatenbank _\r\n                     & \".\" & rstAlt.Fields(0) & <span style=\"color:blue;\">vbCrLf<\/span>\r\n                 strSQLInsert = strSQLInsert _\r\n                     & FelderAbgleichen(rstAlt.Fields(0)) _\r\n                     & <span style=\"color:blue;\">vbCrLf<\/span>\r\n                 ...<\/pre>\n<p>Die Funktion <b>FelderAbgleichen <\/b>finden Sie in Listing 4. Sie erwartet den Namen der zu untersuchenden Tabelle als Parameter. Sie ist grunds&auml;tzlich so aufgebaut wie die Prozedur <b>TabellenAbgleichen<\/b>, nur dass sie diesmal nicht die Tabellen der beiden Datenbanken, sondern die Felder der beiden Tabellen untersucht.<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>FelderAbgleichen(strTabelle<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>rstAlt<span style=\"color:blue;\"> As <\/span>ADODB.Recordset\r\n     <span style=\"color:blue;\">Dim <\/span>rstNeu<span style=\"color:blue;\"> As <\/span>ADODB.Recordset\r\n     <span style=\"color:blue;\">Dim <\/span>strKriterium<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>fld<span style=\"color:blue;\"> As <\/span>ADODB.Field\r\n     <span style=\"color:blue;\">Dim <\/span>strFelder<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> rstAlt = GetRecordset(GetConnection(cStrVerbindungszeichenfolgeAlt), \"DESCRIBE \" & strTabelle)\r\n     <span style=\"color:blue;\">Set<\/span> rstNeu = GetRecordset(GetConnection(cStrVerbindungszeichenfolgeNeu), \"DESCRIBE \" & strTabelle)\r\n     rstAlt.Sort = rstAlt.Fields(\"Field\").Name & \" ASC\"\r\n     rstNeu.Sort = rstNeu.Fields(\"Field\").Name & \" ASC\"\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rstAlt.EOF\r\n         <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> rstNeu.EOF<span style=\"color:blue;\"> Then<\/span>\r\n             <span style=\"color:blue;\">If <\/span>rstAlt.Fields(\"Field\") = rstNeu.Fields(\"Field\")<span style=\"color:blue;\"> Then<\/span>\r\n                 <span style=\"color:blue;\">Debug.Print<\/span> rstAlt.Fields(\"Field\"), rstNeu.Fields(\"Field\")\r\n                 strFelder = strFelder & \", \" & rstAlt.Fields(\"Field\")\r\n                 rstAlt.Move<span style=\"color:blue;\">Next<\/span>\r\n                 rstNeu.Move<span style=\"color:blue;\">Next<\/span>\r\n             <span style=\"color:blue;\">Else<\/span>If rstAlt.Fields(\"Field\") &gt; rstNeu.Fields(\"Field\") Then\r\n                 <span style=\"color:blue;\">Debug.Print<\/span> \"----\", rstNeu.Fields(\"Field\")\r\n                 rstNeu.Move<span style=\"color:blue;\">Next<\/span>\r\n             <span style=\"color:blue;\">Else<\/span>If rstAlt.Fields(\"Field\") &lt; rstNeu.Fields(\"Field\") Then\r\n                 <span style=\"color:blue;\">Debug.Print<\/span> rstAlt.Fields(\"Field\"), \"----\"\r\n                 rstAlt.Move<span style=\"color:blue;\">Next<\/span>\r\n             <span style=\"color:blue;\">End If<\/span>\r\n         <span style=\"color:blue;\">Else<\/span>\r\n             <span style=\"color:blue;\">Debug.Print<\/span> rstAlt.Fields(\"Field\"), \"----\"\r\n             rstAlt.Move<span style=\"color:blue;\">Next<\/span>\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rstNeu.EOF\r\n         <span style=\"color:blue;\">Debug.Print<\/span> \"----\", rstNeu.Fields(\"Field\")\r\n         rstNeu.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;\">Len<\/span>(strFelder) &gt; 0<span style=\"color:blue;\"> Then<\/span>\r\n         strFelder = <span style=\"color:blue;\">Mid<\/span>(strFelder, 3)\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     FelderAbgleichen = \"INSERT INTO \" & cStrNeueDatenbank & \".\" & strTabelle & \"(\" & strFelder & \") SELECT \" _\r\n         & strFelder & \" FROM \" & cStrAlteDatenbank & \".\" & strTabelleEnd Function<\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 4: Die Funktion FelderAbgleichen<\/span><\/b><\/p>\n<p>Daher verwenden wir wieder zwei Recordsets, welche die Verbindungszeichenfolgen aus den Konstanten nutzen. Als Datenquelle verwenden wir diesmal wieder keine klassische <b>SELECT<\/b>-Abfrage, sondern die Anweisung <b>DESCRIBE <\/b>mit den Namen der zu beschreibenden Tabelle. Dies liefert die Namen der Felder der angegebenen Tabelle zur&uuml;ck &#8211; neben einigen anderen Feldern. Uns interessiert aber nur der Feldname, den wir im Feld <b>Field <\/b>finden.<\/p>\n<p>Deshalb sortieren wir die Datens&auml;tze beider Recordsets zuerst aufsteigend nach dem Inhalt des Feldes <b>Field<\/b>. Dann durchlaufen wir in einer <b>Do While<\/b>-Schleife die Datens&auml;tze des Recordsets <b>rstAlt<\/b>. Solange der Datensatzzeiger f&uuml;r <b>rstNeu <\/b>nun nicht auf <b>EOF <\/b>steht, was bedeutet, dass wir zwar noch nicht alle Datens&auml;tze von <b>rstAlt<\/b>, aber schon alle Datens&auml;tze von <b>rstNeu <\/b>durchlaufen haben &#8211; und dementsprechend im <b>Else<\/b>-Teil die erste <b>Do While<\/b>-Schleife verlassen -, bleiben wir in dieser Schleife. Zu Beginn d&uuml;rfte der Datensatzzeiger von <b>rstNeu <\/b>allerdings noch am Anfang stehen, weshalb wir erst den <b>If<\/b>-Teil der Bedingung durchlaufen. Die untergeordnete <b>If&#8230;Then<\/b>-Bedingung &auml;hnelt der aus der Prozedur <b>TabellenAbgleichen<\/b>.<\/p>\n<p>Sie pr&uuml;ft, ob ein Feld in beiden Tabellen oder nur in einer der beiden Tabellen vorhanden ist und liefert entsprechende Ausgaben im Direktbereich und schiebt den Datensatzzeiger des jeweils betroffenen Recordsets weiter. Im <b>If<\/b>-Teil haben wir eine Anweisung eingebaut, die der Variablen <b>strFelder <\/b>ein Komma und den Namen des in beiden Tabellen vorkommenden Feldes hinzuf&uuml;gt.<\/p>\n<p>Dadurch erhalten wir so Schritt f&uuml;r Schritt eine komma-separierte Liste der Feldnamen. In der zweiten <b>Do While<\/b>-Schleife durchlaufen wir schlie&szlig;lich noch alle Datens&auml;tze von <b>rstNeu<\/b>, die nach dem vollst&auml;ndigen Durchlaufen von <b>rstAlt <\/b>noch &uuml;brig sind.<\/p>\n<p>Danach pr&uuml;fen wir, ob mindestens ein Feldname in <b>strFelder<\/b> vorhanden ist &#8211; etwa so:<\/p>\n<pre>, datum, id, referer<\/pre>\n<p>Dann schneiden wir das f&uuml;hrende Komma ab, damit der Inhalt von <b>strFelder <\/b>etwa wie folgt aussieht:<\/p>\n<pre>datum, id, referer<\/pre>\n<p>Den Inhalt von <b>strFelder <\/b>f&uuml;gen wir dann in den R&uuml;ckgabewert der Funktion <b>FelderAbgleichen<\/b>.<\/p>\n<p>Dort verwenden wir den Wert zweier weiterer Konstanten namens <b>cStrNeueDatenbank <\/b>und <b>cStrAlteDatenbank<\/b>, die wir wie folgt deklarieren:<\/p>\n<pre>Const cStrAlteDatenbank<span style=\"color:blue;\"> As String<\/span> = \"&lt;Alte Datenbank&gt;\"\r\nConst cStrNeueDatenbank<span style=\"color:blue;\"> As String<\/span> = \"&lt;Neue Datenbank&gt;\"<\/pre>\n<p>Das Ergebnis sieht dann etwa wie folgt aus:<\/p>\n<pre>INSERT INTO &lt;Neue Datenbank&gt;.&lt;Tabelle&gt;(&lt;Feld1&gt;, &lt;Feld2&gt;, ..., &lt;FeldN&gt;) SELECT &lt;Feld1&gt;, &lt;Feld2&gt;, ..., &lt;FeldN&gt; FROM &lt;Alte Datenbank&gt;.&lt;Tabelle&gt;<\/pre>\n<p>Die Prozedur <b>TabellenAbgleichen <\/b>stellt durch einen Aufruf der Funktion <b>FelderAbgleichen <\/b>f&uuml;r jede Tabelle, die in der alten und der neuen Version der Datenbank auftaucht,  eine <b>INSERT INTO<\/b>-Abfrage zusammen. Diese werden dann mithilfe der Prozedur <b>InZwischenablage<\/b> aus dem Modul <b>mdlZwischenablage <\/b>in die Zwischenablage kopiert, damit Sie diese weiterverwenden k&ouml;nnen.<\/p>\n<p><b>SQL-Anweisungen einsetzen<\/b><\/p>\n<p>Im Falle meines Shops steht die <b>phpMyAdmin<\/b>-Benutzeroberfl&auml;che f&uuml;r den Zugriff auf die Datenbank des Internetservers zur Verf&uuml;gung.<\/p>\n<p>Hier k&ouml;nnen wir eine Datenbank ausw&auml;hlen und unter SQL den Inhalt der Zwischenablage einf&uuml;gen (siehe Bild 2).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2019_05\/pic_1208_002.png\" alt=\"Ausf&uuml;hren von SQL-Anweisungen auf dem Webserver\" width=\"649,559\" height=\"506,01\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Ausf&uuml;hren von SQL-Anweisungen auf dem Webserver<\/span><\/b><\/p>\n<p><b>Reihenfolge beachten<\/b><\/p>\n<p>In unserem Fall treten Fehler auf, weil zwischen den Tabellen Beziehungen definiert sind und wir versucht haben, Datens&auml;tze einer Tabelle zu l&ouml;schen, mit denen noch Datens&auml;tze anderer Tabellen verkn&uuml;pft sind:<\/p>\n<pre>#1451 - Kann Eltern-Zeile nicht l&ouml;schen oder aktualisieren: eine Fremdschl&uuml;sselbedingung schl&auml;gt fehl ('amvshop_sw54'.'s_user_addresses', CONSTRAINT 's_user_addresses_ibfk_1' FOREIGN KEY ('country_id') REFERENCES 's_core_countries' ('id') ON UPDATE CASCADE)<\/pre>\n<p>Wir m&uuml;ssen also noch die Reihenfolge beim L&ouml;schen und auch beim Kopieren der Datens&auml;tze ber&uuml;cksichtigen. Wie bekommen wir das m&ouml;glichst automatisiert hin<\/p>\n<p>Den gr&ouml;&szlig;ten Teil der Aufgabe haben wir bereits einmal erledigt, und zwar im Beitrag <b>L&ouml;sch- und Kopierreihenfolge f&uuml;r Tabellen <\/b>(<b>www.access-im-unternehmen.de\/926<\/b>). Allerdings haben wir hier auf die Tabellen einer Access-Datenbank zugegriffen, und zwar &uuml;ber die Auflistung <b>TableDefs<\/b>. Und wir konnten dort per DAO auf die Verkn&uuml;pfungseigenschaften der Tabellen zugreifen, was wir auch bei per ODBC verkn&uuml;pften Tabellen nicht erledigen k&ouml;nnen.<\/p>\n<p>Wie Sie die Reihenfolge von Tabellen zum Kopieren oder L&ouml;schen in einer MySQL-Datenbank ermitteln, erl&auml;utern wir im Beitrag <b>L&ouml;sch- und Kopierreihenfolge in MySQL <\/b>(<b>www.access-im-unternehmen.de\/1207<\/b>). Hier haben wir zwei Funktionen programmiert:<\/p>\n<ul>\n<li><b>TabellenreihenfolgeKopieren <\/b>liefert die Reihenfolge f&uuml;r das Kopieren der Tabellen f&uuml;r eine MySQL-Datenbank mit einer bestimmten Verbindungszeichenfolge als <b>Collection<\/b>.<\/li>\n<li><b>TabellenreihenfolgeLoeschen <\/b>liefert die Reihenfolge f&uuml;r das L&ouml;schen der Tabellen f&uuml;r eine MySQL-Datenbank mit einer bestimmten Verbindungszeichenfolge als <b>Collection<\/b>.<\/li>\n<\/ul>\n<p>Bisher hat unsere Prozedur <b>TabellenAbgleichen <\/b>das Ergebnis in Form der <b>DELETE<\/b>&#8211; und <b>INSERT<\/b>-Anweisungen in die Zwischenablage geschrieben. Wenn wir nun die von den beiden oben genannten Funktionen gelieferten <b>Collection<\/b>-Objekte mit den Tabellen in der richtigen Reihenfolge nutzen wollen, haben wir zwei M&ouml;glichkeiten:<\/p>\n<ul>\n<li>Wir bohren die Prozedur <b>TabellenAbgleichen <\/b>komplett auf und integrieren die Funktionen darin. Das wird vermutlich sehr aufwendig.<\/li>\n<li>Wir wandeln die Prozedur <b>TabellenAbgleichen <\/b>in eine Funktion, welche die Tabellen und die <b>DELETE<\/b>&#8211; und <b>INSERT<\/b>-Anweisungen auch als Collection zur&uuml;ckliefert und bringen die beiden Ergebnisse in einer weiteren Prozedur zusammen. Das h&ouml;rt sich nach weniger Aufwand an.<\/li>\n<\/ul>\n<p>Es hilft nur leider nichts, wenn die neue Version der Routine, die wir dann als Funktion <b>TabellenAbgleichen_DeleteInsert <\/b>nennen, nur eine Collection der Tabellen zur&uuml;ckliefert. Wir ben&ouml;tigen ja auch die <b>DELETE<\/b>&#8211; und die <b>INSERT<\/b>-Anweisungen, die dort ermittelt werden.<\/p>\n<p><b>Klasse zum Speichern von Tabelle und DELETE- und INSERT-Anweisung<\/b><\/p>\n<p>Da wir nicht alles drei standardm&auml;&szlig;ig als Text innerhalb einer einzigen Collection zur&uuml;ckliefern k&ouml;nnen, bauen wir uns eine Klasse zusammen, welche die drei Informationen aufnimmt.<\/p>\n<p>Diese legen wir unter dem Namen <b>clsTabelleDeleteInsert <\/b>an und f&uuml;llen das Klassenmodul mit dem folgenden Code:<\/p>\n<pre><span style=\"color:blue;\">Private <\/span>m_Tabelle<span style=\"color:blue;\"> As String<\/span>\r\n<span style=\"color:blue;\">Private <\/span>m_Delete<span style=\"color:blue;\"> As String<\/span>\r\n<span style=\"color:blue;\">Private <\/span>m_Insert<span style=\"color:blue;\"> As String<\/span>\r\n<span style=\"color:blue;\">Public Property Let <\/span>Tabelle(strTabelle<span style=\"color:blue;\"> As String<\/span>)\r\n     m_Tabelle = strTabelle\r\n<span style=\"color:blue;\">End Property<\/span>\r\n<span style=\"color:blue;\">Public Property Get <\/span>Tabelle()<span style=\"color:blue;\"> As String<\/span>\r\n     Tabelle = m_Tabelle\r\n<span style=\"color:blue;\">End Property<\/span>\r\n<span style=\"color:blue;\">Public Property Let <\/span>Delete(strDelete<span style=\"color:blue;\"> As String<\/span>)\r\n     m_Delete = strDelete\r\n<span style=\"color:blue;\">End Property<\/span>\r\n<span style=\"color:blue;\">Public Property Get <\/span>Delete()<span style=\"color:blue;\"> As String<\/span>\r\n     Delete = m_Delete\r\n<span style=\"color:blue;\">End Property<\/span>\r\n<span style=\"color:blue;\">Public Property Let <\/span>Insert(strInsert<span style=\"color:blue;\"> As String<\/span>)\r\n     m_Insert = strInsert\r\n<span style=\"color:blue;\">End Property<\/span>\r\n<span style=\"color:blue;\">Public Property Get <\/span>Insert()<span style=\"color:blue;\"> As String<\/span>\r\n     Insert = m_Insert\r\n<span style=\"color:blue;\">End Property<\/span><\/pre>\n<p>Damit erhalten wir drei private Variablen, denen wir &uuml;ber die <b>Property Let<\/b>-Eigenschaften die Werte f&uuml;r den Tabellennamen, die <b>DELETE<\/b>-Anweisung f&uuml;r diese Tabelle und die <b>INSERT<\/b>-Anweisung f&uuml;r diese Tabelle hinzuf&uuml;gen k&ouml;nnen. &Uuml;ber die <b>Property Get<\/b>-Prozeduren k&ouml;nnen wir diese Werte dann wieder auslesen.<\/p>\n<p><b>Die Funktion zum Ermitteln von Tabelle und DELETE- und INSERT-Anweisung<\/b><\/p>\n<p>Die Funktion <b>TabellenAbgleichen_DeleteInsert<\/b> sieht nun wie in Listing 5 aus. Sie deklariert nun eine zus&auml;tliche Variable namens <b>objTabelleDeleteInsert <\/b>mit dem Datentyp <b>clsTabelleDeleteInsert <\/b>sowie die Collection <b>colTabellen<\/b>. Sie verwendet die gleichen Schleifen wir die vorherige Prozedur <b>TabellenAbgleichen<\/b> und stellt die <b>DELETE<\/b>&#8211; und die <b>INSERT<\/b>-Anweisung auf die gleiche Weise zusammen.<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>TabellenAbgleichen_ReihenfolgeDeleteInsert()<span style=\"color:blue;\"> As <\/span>Collection\r\n     <span style=\"color:blue;\">Dim <\/span>rstAlt<span style=\"color:blue;\"> As <\/span>ADODB.Recordset\r\n     <span style=\"color:blue;\">Dim <\/span>rstNeu<span style=\"color:blue;\"> As <\/span>ADODB.Recordset\r\n     <span style=\"color:blue;\">Dim <\/span>strSQLDelete<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strSQLInsert<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>colTabellen<span style=\"color:blue;\"> As <\/span>Collection\r\n     <span style=\"color:blue;\">Dim <\/span>objTabelleDeleteInsert<span style=\"color:blue;\"> As <\/span>clsTabelleDeleteInsert\r\n     <span style=\"color:blue;\">Set<\/span> colTabellen = <span style=\"color:blue;\">New<\/span> Collection\r\n     <span style=\"color:blue;\">Set<\/span> rstAlt = GetRecordset(GetConnection(cStrVerbindungszeichenfolgeAlt), \"SHOW TABLES\")\r\n     <span style=\"color:blue;\">Set<\/span> rstNeu = GetRecordset(GetConnection(cStrVerbindungszeichenfolgeNeu), \"SHOW TABLES\")\r\n     rstAlt.Sort = rstAlt.Fields(0).Name & \" ASC\"\r\n     rstNeu.Sort = rstNeu.Fields(0).Name & \" ASC\"\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rstAlt.EOF\r\n         <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> rstNeu.EOF<span style=\"color:blue;\"> Then<\/span>\r\n             <span style=\"color:blue;\">If <\/span>rstAlt.Fields(0) = rstNeu.Fields(0)<span style=\"color:blue;\"> Then<\/span>\r\n                 strSQLDelete = \"DELETE FROM \" & cStrNeueDatenbank & \".\" & rstAlt.Fields(0) & \";\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n                 strSQLInsert = FelderAbgleichen(rstAlt.Fields(0)) & \";\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n                 <span style=\"color:blue;\">Set<\/span> objTabelleDeleteInsert = <span style=\"color:blue;\">New<\/span> clsTabelleDeleteInsert\r\n                 <span style=\"color:blue;\">With<\/span> objTabelleDeleteInsert\r\n                     .Tabelle = rstAlt.Fields(0)\r\n                     .Delete = strSQLDelete\r\n                     .Insert = strSQLInsert\r\n                 End <span style=\"color:blue;\">With<\/span>\r\n                 colTabellen.Add objTabelleDeleteInsert, rstAlt.Fields(0).Value\r\n                 rstAlt.Move<span style=\"color:blue;\">Next<\/span>\r\n                 rstNeu.Move<span style=\"color:blue;\">Next<\/span>\r\n             <span style=\"color:blue;\">Else<\/span>If rstAlt.Fields(0) &gt; rstNeu.Fields(0) Then\r\n                 rstNeu.Move<span style=\"color:blue;\">Next<\/span>\r\n             <span style=\"color:blue;\">Else<\/span>If rstAlt.Fields(0) &lt; rstNeu.Fields(0) Then\r\n                 rstAlt.Move<span style=\"color:blue;\">Next<\/span>\r\n             <span style=\"color:blue;\">End If<\/span>\r\n         <span style=\"color:blue;\">Else<\/span>\r\n             rstAlt.Move<span style=\"color:blue;\">Next<\/span>\r\n         <span style=\"color:blue;\">End If<\/span>\r\n         DoEvents\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rstNeu.EOF\r\n         rstNeu.Move<span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> TabellenAbgleichen_ReihenfolgeDeleteInsert = colTabellen\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 5: Die Funktion TabellenAbgleichen_ReihenfolgeDeleteInsert<\/span><\/b><\/p>\n<p>F&uuml;r jede Tabelle erstellt sie danach jedoch eine neue Instanz der Klasse <b>clsTabelleDeleteInsert<\/b>, der wir f&uuml;r die Eigenschaft <b>Tabelle <\/b>den Tabellennamen aus <b>rstAlt!Fields(0)<\/b>, f&uuml;r die Eigenschaft <b>Delete <\/b>den Inhalt aus <b>strSQLDelete <\/b>und f&uuml;r die Eigenschaft <b>Insert <\/b>den Inhalt aus <b>strSQLInsert <\/b>zuweist.<\/p>\n<p>Danach f&uuml;gen wir das neue Objekt aus <b>objTabelleDeleteInsert <\/b>mit der <b>Add<\/b>-Methode zur Collection <b>colTabellen <\/b>hinzu, wobei wir als <b>Key <\/b>noch den Namen der Tabelle angeben. Diese Collection geben wir schlie&szlig;lich als Funktionswert zur&uuml;ck.<\/p>\n<p><b>DELETE-Anweisungen in die richtige Reihenfolge bringen<\/b><\/p>\n<p>Die Funktion <b>TabellenAbgleichen_DeleteInsert <\/b>liefert nun eine Collection mit Objekten des Typs <b>clsTabelleDeleteInsert<\/b>, die wir noch in die Reihenfolge bringen m&uuml;ssen, die wir den <b>Collection<\/b>-Objekten entnehmen k&ouml;nnen, welche die Funktionen <b>TabellenreihenfolgeLoeschen <\/b>und <b>TabellenreihenfolgeKopieren<\/b> liefern. <\/p>\n<p>Das Zusammenf&uuml;hren der beiden <b>Collection<\/b>-Objekte erledigen wir jeweils in einer eigenen Prozedur. Die erste hei&szlig;t <b>SQLAnweisungenDELETEInZwischenablage<\/b>. Diese Prozedur finden Sie in Listing 6. Die Prozedur deklariert die beiden <b>Collection<\/b>-Objekte <b>colTabellen <\/b>und <b>colReihenfolge<\/b>, die sie dann mithilfe der Funktionen <b>TabelleAbgleichen_Reihenfolge <\/b>und <b>TabellenreihenfolgeLoeschen <\/b>f&uuml;llt.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>SQLAnweisungenDELETEInZwischenablage()\r\n     <span style=\"color:blue;\">Dim <\/span>colTabellen<span style=\"color:blue;\"> As <\/span>Collection\r\n     <span style=\"color:blue;\">Dim <\/span>colReihenfolge<span style=\"color:blue;\"> As <\/span>Collection\r\n     <span style=\"color:blue;\">Dim <\/span>objTabelleDeleteInsert<span style=\"color:blue;\"> As <\/span>clsTabelleDeleteInsert\r\n     <span style=\"color:blue;\">Dim <\/span>varReihenfolge<span style=\"color:blue;\"> As Variant<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strSQLDelete<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> colTabellen = TabellenAbgleichen_ReihenfolgeDeleteInsert\r\n     <span style=\"color:blue;\">Set<\/span> colReihenfolge = TabellenreihenfolgeLoeschen(cStrVerbindungszeichenfolgeNeu)\r\n     For Each varReihenfolge In colReihenfolge\r\n         <span style=\"color:blue;\">Set<\/span> objTabelleDeleteInsert = Nothing\r\n         On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n         <span style=\"color:blue;\">Set<\/span> objTabelleDeleteInsert = colTabellen.Item(varReihenfolge)\r\n         <span style=\"color:blue;\">On Error GoTo<\/span> 0\r\n         <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> objTabelleDeleteInsert Is Nothing<span style=\"color:blue;\"> Then<\/span>\r\n             strSQLDelete = strSQLDelete & objTabelleDeleteInsert.Delete\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">Next<\/span> varReihenfolge\r\n     Inzwischenablage strSQLDelete\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 6: Die Funktion SQLAnweisungenDELETEInZwischenablage<\/span><\/b><\/p>\n<p>Dann durchl&auml;uft die Prozedur die Elemente der Collection <b>colReihenfolge<\/b>, welche die Tabellen in der f&uuml;r das L&ouml;schen richtigen Reihenfolge liefert, in einer <b>For Each<\/b>-Schleife. Darin leert sie die Variable <b>objTabelleDeleteInsert<\/b>, die anschlie&szlig;end mit dem Objekt der Collection <b>colTabellen <\/b>gef&uuml;llt wird, deren Index dem Tabellennamen aus <b>colReihenfolge <\/b>entspricht. Da die Collection <b>colTabellen <\/b>nur die Tabellen enth&auml;lt, die in der Quelldatenbank und in der Zieldatenbank vorliegen, und die Collection <b>colReihenfolge <\/b>alle Tabellen, kann es sein, dass f&uuml;r eine Tabelle aus <b>colReihenfolge <\/b>kein Element in <b>colTabellen <\/b>gefunden werden kann. Deshalb deaktivieren wir die eingebaute Fehlerbehandlung und pr&uuml;fen nach der Zuweisung, ob &uuml;berhaupt ein passendes Element in <b>colTabellen <\/b>vorlag. Ist das der Fall, f&uuml;gen wir an die <b>String<\/b>-Variable <b>strSQLDelete <\/b>den Inhalt der Eigenschaft <b>Delete <\/b>des Objekts aus <b>objTabelleDeleteInsert <\/b>ein. Auf diese Weise durchlaufen wir in der Schleife alle gemeinsamen Tabellen und stellen alle ben&ouml;tigten <b>DELETE<\/b>-Anweisungen in der Variablen <b>strSQLDelete <\/b>zusammen. Diese kopieren wir dann mit der Prozedur <b>InZwischenablage <\/b>in die Zwischen-ablage. Von dieser k&ouml;nnen Sie die <b>DELETE<\/b>-Anweisungen dann wie oben beschrieben in das SQL-Feld etwa von <b>phpMyAdmin <\/b>einf&uuml;gen und ausf&uuml;hren lassen.<\/p>\n<p>Die Prozedur zum Zusammenstellen der SQL-Anweisungen zum Kopieren der Datens&auml;tze aus der Quelldatenbank in die Zieldatenbank finden Sie unter dem Namen <b>SQLAnweisungenINSERTInZwischenablage <\/b>im Modul <b>mdlADODB <\/b>der Beispieldatenbank. Sie ist genauso aufgebaut wie die soeben beschriebene Prozedur, stellt aber die in der Eigenschaft <b>Insert <\/b>des jeweiligen <b>clsTabelleDeleteInsert<\/b>-Objekts enthaltenen SQL-Anweisungen in der Variablen <b>strSQLInsert <\/b>zusammen und &uuml;bertr&auml;gt sie anschlie&szlig;end in die Zwischenablage.<\/p>\n<p>Damit sind die automatischen Schritte zum Erstellen von <b>DELETE<\/b>&#8211; und <b>INSERT<\/b>-Anweisungen abgeschlossen.<\/p>\n<p><b>Sichern, testen und anpassen<\/b><\/p>\n<p>Bevor Sie die Anweisungen ausprobieren, sollten Sie eine Sicherung der Zieldatenbank erstellen. So k&ouml;nnen Sie eventuell gel&ouml;schte Daten schnell wiederherstellen. Au&szlig;erdem wird noch eine Menge Handarbeit anfallen. So werden Sie feststellen, dass eventuell Tabellen umbenannt wurden oder Felder in der neuen Version von einer Tabelle in andere Tabellen verschoben wurden. Das hei&szlig;t, Sie m&uuml;ssen immer noch genau pr&uuml;fen, ob die neue Anwendung mit den von Ihnen importierten Daten der alten Version so funktioniert wie erwartet.<\/p>\n<p>Au&szlig;erdem kann es sein, dass Tabellen zwar in der alten und der neuen Version identisch sind, aber dass diese Systemeinstellungen enthalten, die in der neuen Version anders lauten als in der alten Version. In einem solchen Fall d&uuml;rfen Sie nat&uuml;rlich nicht einfach die Tabelle der neuen Version mit <b>DELETE <\/b>leeren und die Daten der entsprechenden Tabelle der alten Version &uuml;berschreiben. Solche F&auml;lle werden aber vermutlich beim Testen der neuen Version nach dem Import der Daten auffallen.<\/p>\n<p>Schlie&szlig;lich werden Sie mit mehr oder weniger Arbeit ein Update-Skript erhalten, mit dem Sie die Daten der alten Version auf die neue Version &uuml;bertragen k&ouml;nnen.<\/p>\n<h3>Downloads zu diesem Beitrag<\/h3>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>VonVersionZuVersion.accdb<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/{2D5FCF16-7919-406A-B50C-17491A927162}\/aiu_1208.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Neulich wollte ich meine Shopsoftware aktualisieren. Allerdings ging dies nicht mit den vom Hersteller daf&uuml;r bereitgestellten Plug-In &#8211; zumindest nicht mit allen Daten, die ich von der alten in die neue Version &uuml;berf&uuml;hren wollte. Also musste ich manuell ermitteln, welche Daten der alten Version ben&ouml;tigt werden, damit die neue Version l&auml;uft. Damit erhielt ich recht schnell eine lauff&auml;hige Version des neuen Systems. Allerdings kam mir dann etwas dazwischen, wodurch zum alten, noch aktiven System wieder neue Kunden und Bestellungen hinzukamen &#8211; die Arbeit war also umsonst. Um das &Uuml;bertragen der Daten beim n&auml;chsten Mal einfacher zu gestalten, wollte ich nun das alte und das neue Datenmodell nun automatisch analysieren und die SQL-Befehle erstellen lassen, um diese danach ebenfalls automatisch ausf&uuml;hren zu k&ouml;nnen. Wie das gelingt, zeigt der vorliegende Beitrag.<\/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":[662019,66052019,44000026],"tags":[],"class_list":["post-55001208","post","type-post","status-publish","format-standard","hentry","category-662019","category-66052019","category-Interaktiv"],"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>Von Version zu Version - 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\/Von_Version_zu_Version\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Von Version zu Version\" \/>\n<meta property=\"og:description\" content=\"Neulich wollte ich meine Shopsoftware aktualisieren. Allerdings ging dies nicht mit den vom Hersteller daf&uuml;r bereitgestellten Plug-In - zumindest nicht mit allen Daten, die ich von der alten in die neue Version &uuml;berf&uuml;hren wollte. Also musste ich manuell ermitteln, welche Daten der alten Version ben&ouml;tigt werden, damit die neue Version l&auml;uft. Damit erhielt ich recht schnell eine lauff&auml;hige Version des neuen Systems. Allerdings kam mir dann etwas dazwischen, wodurch zum alten, noch aktiven System wieder neue Kunden und Bestellungen hinzukamen - die Arbeit war also umsonst. Um das &Uuml;bertragen der Daten beim n&auml;chsten Mal einfacher zu gestalten, wollte ich nun das alte und das neue Datenmodell nun automatisch analysieren und die SQL-Befehle erstellen lassen, um diese danach ebenfalls automatisch ausf&uuml;hren zu k&ouml;nnen. Wie das gelingt, zeigt der vorliegende Beitrag.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/access-im-unternehmen.de\/Von_Version_zu_Version\/\" \/>\n<meta property=\"og:site_name\" content=\"Access im Unternehmen\" \/>\n<meta property=\"article:published_time\" content=\"2020-05-29T12:07:20+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/vg06.met.vgwort.de\/na\/277900bba37b4dc3bb1008993dfa25d3\" \/>\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=\"21\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Von_Version_zu_Version\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Von_Version_zu_Version\\\/\"},\"author\":{\"name\":\"Andr\u00e9 Minhorst\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\"},\"headline\":\"Von Version zu Version\",\"datePublished\":\"2020-05-29T12:07:20+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Von_Version_zu_Version\\\/\"},\"wordCount\":3285,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Von_Version_zu_Version\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg06.met.vgwort.de\\\/na\\\/277900bba37b4dc3bb1008993dfa25d3\",\"articleSection\":[\"2019\",\"5\\\/2019\",\"Interaktiv\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Von_Version_zu_Version\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Von_Version_zu_Version\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Von_Version_zu_Version\\\/\",\"name\":\"Von Version zu Version - Access im Unternehmen\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Von_Version_zu_Version\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Von_Version_zu_Version\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg06.met.vgwort.de\\\/na\\\/277900bba37b4dc3bb1008993dfa25d3\",\"datePublished\":\"2020-05-29T12:07:20+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Von_Version_zu_Version\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Von_Version_zu_Version\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Von_Version_zu_Version\\\/#primaryimage\",\"url\":\"http:\\\/\\\/vg06.met.vgwort.de\\\/na\\\/277900bba37b4dc3bb1008993dfa25d3\",\"contentUrl\":\"http:\\\/\\\/vg06.met.vgwort.de\\\/na\\\/277900bba37b4dc3bb1008993dfa25d3\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Von_Version_zu_Version\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Von Version zu Version\"}]},{\"@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":"Von Version zu Version - 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\/Von_Version_zu_Version\/","og_locale":"de_DE","og_type":"article","og_title":"Von Version zu Version","og_description":"Neulich wollte ich meine Shopsoftware aktualisieren. Allerdings ging dies nicht mit den vom Hersteller daf&uuml;r bereitgestellten Plug-In - zumindest nicht mit allen Daten, die ich von der alten in die neue Version &uuml;berf&uuml;hren wollte. Also musste ich manuell ermitteln, welche Daten der alten Version ben&ouml;tigt werden, damit die neue Version l&auml;uft. Damit erhielt ich recht schnell eine lauff&auml;hige Version des neuen Systems. Allerdings kam mir dann etwas dazwischen, wodurch zum alten, noch aktiven System wieder neue Kunden und Bestellungen hinzukamen - die Arbeit war also umsonst. Um das &Uuml;bertragen der Daten beim n&auml;chsten Mal einfacher zu gestalten, wollte ich nun das alte und das neue Datenmodell nun automatisch analysieren und die SQL-Befehle erstellen lassen, um diese danach ebenfalls automatisch ausf&uuml;hren zu k&ouml;nnen. Wie das gelingt, zeigt der vorliegende Beitrag.","og_url":"https:\/\/access-im-unternehmen.de\/Von_Version_zu_Version\/","og_site_name":"Access im Unternehmen","article_published_time":"2020-05-29T12:07:20+00:00","og_image":[{"url":"http:\/\/vg06.met.vgwort.de\/na\/277900bba37b4dc3bb1008993dfa25d3","type":"","width":"","height":""}],"author":"Andr\u00e9 Minhorst","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Andr\u00e9 Minhorst","Gesch\u00e4tzte Lesezeit":"21\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/access-im-unternehmen.de\/Von_Version_zu_Version\/#article","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/Von_Version_zu_Version\/"},"author":{"name":"Andr\u00e9 Minhorst","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f"},"headline":"Von Version zu Version","datePublished":"2020-05-29T12:07:20+00:00","mainEntityOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Von_Version_zu_Version\/"},"wordCount":3285,"commentCount":0,"publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Von_Version_zu_Version\/#primaryimage"},"thumbnailUrl":"http:\/\/vg06.met.vgwort.de\/na\/277900bba37b4dc3bb1008993dfa25d3","articleSection":["2019","5\/2019","Interaktiv"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/access-im-unternehmen.de\/Von_Version_zu_Version\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/access-im-unternehmen.de\/Von_Version_zu_Version\/","url":"https:\/\/access-im-unternehmen.de\/Von_Version_zu_Version\/","name":"Von Version zu Version - Access im Unternehmen","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Von_Version_zu_Version\/#primaryimage"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Von_Version_zu_Version\/#primaryimage"},"thumbnailUrl":"http:\/\/vg06.met.vgwort.de\/na\/277900bba37b4dc3bb1008993dfa25d3","datePublished":"2020-05-29T12:07:20+00:00","breadcrumb":{"@id":"https:\/\/access-im-unternehmen.de\/Von_Version_zu_Version\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/access-im-unternehmen.de\/Von_Version_zu_Version\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/Von_Version_zu_Version\/#primaryimage","url":"http:\/\/vg06.met.vgwort.de\/na\/277900bba37b4dc3bb1008993dfa25d3","contentUrl":"http:\/\/vg06.met.vgwort.de\/na\/277900bba37b4dc3bb1008993dfa25d3"},{"@type":"BreadcrumbList","@id":"https:\/\/access-im-unternehmen.de\/Von_Version_zu_Version\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/access-im-unternehmen.de\/"},{"@type":"ListItem","position":2,"name":"Von Version zu Version"}]},{"@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\/55001208","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=55001208"}],"version-history":[{"count":0,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55001208\/revisions"}],"wp:attachment":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/media?parent=55001208"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/categories?post=55001208"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/tags?post=55001208"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}