Von Version zu Version

Neulich wollte ich meine Shopsoftware aktualisieren. Allerdings ging dies nicht mit den vom Hersteller dafür bereitgestellten Plug-In – zumindest nicht mit allen Daten, die ich von der alten in die neue Version überführen wollte. Also musste ich manuell ermitteln, welche Daten der alten Version benötigt werden, damit die neue Version läuft. Damit erhielt ich recht schnell eine lauffä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 übertragen der Daten beim nä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ühren zu können. Wie das gelingt, zeigt der vorliegende Beitrag.

Bei der Shopsoftware handelt es sich um ein Produkt namens Shopware. Es verwendet eine MySQL-Datenbank.

Die hier vorgestellten Techniken lassen sich jedoch auch auf andere Systeme übertragen – und auch auf andere Anwendungen.

Auch MySQL als Datenbanksystem kann durch andere Systeme ersetzt werden – Sie müssen dann schlicht den Treiber in der Verbindungszeichenfolge austauschen und gegebenenfalls ein paar Abfragen auf spezifische Eigenarten des jeweiligen Dialekts anpassen. Voraussetzung für die Nutzung der hier vorgestellten Technik ist der Zugriff auf die Datenbank. Ob dies möglich ist, müssen Sie mit dem jeweiligen Provider klären.

Zugriff per ADODB

Damit wir per ODBC auf die Daten der beiden zu vergleichenden MySQL-Datenbanken zugreifen können, fügen wir der Datenbank einen Verweis auf die Bibliothek Microsoft ActiveX Data Objects 2.8 Library zum VBA-Projekt einer neuen Access-Datenbank hinzu (siehe Bild 1).

Verweis auf die ADODB-Bibliothek

Bild 1: Verweis auf die ADODB-Bibliothek

Tabellen abgleichen

Als Erstes wollen wir die Tabellen der beiden Datenbanken abgleichen, also prüfen, ob es in der ersten Datenbank Tabellen gibt, die nicht in der zweiten Datenbank vorkommen und umgekehrt.

Dazu legen wir zwei Konstanten mit den Verbindungszeichenfolgen der beiden Datenbanken in einem neuen Modul namens mdlADODB an:

Const cStrVerbindungszeichenfolgeAlt As String = "DRIVER={MySQL ODBC 5.3 ANSI Driver};SERVER=xxx.xxx.xxx.xxx;DATABASE=<Datenbankname>;UID=<Benutzername>;PWD=<Kennwort>"
Const cStrVerbindungszeichenfolgeNeu As String = "DRIVER={MySQL ODBC 5.3 ANSI Driver};SERVER=xxx.xxx.xxx.xxx;DATABASE=<Datenbankname>;UID=<Benutzername>;PWD=<Kennwort>"

Die Funktion GetConnection erwartet die Angabe der zu verwendenden Verbindungszeichenfolge als Parameter. Sie erstellt ein neues Objekt des Typs ADODB.Connection und öffnet diese unter Angabe der Verbindungszeichenfolge (siehe Listing 1).

Public Function GetConnection(strConnection As String) As ADODB.Connection
     Dim objConnection As ADODB.Connection
     Set objConnection = New ADODB.Connection
     objConnection.Open strConnection
     Set GetConnection = objConnection
End Function

Listing 1: Die Funktion GetConnection

Die Funktion GetRecordset erwartet ein ADODB.Connection-Objekt, das wir zuvor beispielsweise mit der Funktion GetConnection ermitteln, sowie eine SQL-Anweisung mit der zu verwendenden Abfrage (siehe Listing 2). Die Funktion erstellt ein neues ADODB.Recordset-Objekt und stellt seine Eigenschaften ein, darunter auch die SQL-Abfrage für die Eigenschaft Source. Das Ergebnis wird dann als ADODB.Recordset-Objekt zurückgegeben.

Public Function GetRecordset(objConnection As ADODB.Connection, strSQL As String) _
         As ADODB.Recordset
     Dim rst As ADODB.Recordset
     Set rst = New ADODB.Recordset
     With rst
         .ActiveConnection = objConnection
         .CursorLocation = adUseClient
         .CursorType = adOpenDynamic
         .LockType = adLockBatchOptimistic
         .Source = strSQL
         .Open , , , , adCmdText
         Set GetRecordset = rst
     End With
End Function

Listing 2: Die Funktion GetRecordset

Danach nutzen wir diese beiden Funktionen in einer neuen Prozedur namens TabellenAbgleichen (siehe Listing 3). Diese Prozedur verwendet zwei ADODB.Recordset-Objekte namens rstAlt und rstNeu, um die Namen der Tabellen der alten und der neuen Version der Datenbank zu speichern. Diese füllen wir jeweils mit einem Aufruf der Funktion GetRecordset, wobei wir einmal die Verbindungszeichenfolge aus cStrVerbindungszeichenfolgeAlt und einmal die aus cStrVerbindungszeichenfolgeNeu übergeben. In beiden Fällen geben wir als SQL-Anweisung keine einfache SELECT-Anweisung an, sondern die Anweisung Show Tables. Dies ist eine MySQL-spezifische Anweisung, die alle Tabellen einer Datenbank liefert. Danach sortieren wir die Inhalte der beiden Recordsets rstAlt und rstNeu aufsteigend nach dem Inhalt des einzigen Feldes.

Public Sub TabellenAbgleichen()
     Dim rstAlt As ADODB.Recordset
     Dim rstNeu As ADODB.Recordset
     Dim strKriterium As String
     Dim fld As ADODB.Field
     Set rstAlt = GetRecordset(GetConnection(cStrVerbindungszeichenfolgeAlt), "SHOW TABLES")
     Set rstNeu = GetRecordset(GetConnection(cStrVerbindungszeichenfolgeNeu), "SHOW TABLES")
     rstAlt.Sort = rstAlt.Fields(0).Name & " ASC"
     rstNeu.Sort = rstNeu.Fields(0).Name & " ASC"
     Do While Not rstAlt.EOF
         If Not rstNeu.EOF Then
             If rstAlt.Fields(0) = rstNeu.Fields(0) Then
                 Debug.Print rstAlt.Fields(0), rstNeu.Fields(0)
                 rstAlt.MoveNext
                 rstNeu.MoveNext
             ElseIf rstAlt.Fields(0) > rstNeu.Fields(0) Then
                 Debug.Print "----", rstNeu.Fields(0)
                 rstNeu.MoveNext
             ElseIf rstAlt.Fields(0) < rstNeu.Fields(0) Then
                 Debug.Print rstAlt.Fields(0), "----"
                 rstAlt.MoveNext
             End If
         Else
             Debug.Print rstAlt.Fields(0), "----"
             rstAlt.MoveNext
         End If
     Loop
     Do While Not rstNeu.EOF
         Debug.Print "----", rstNeu.Fields(0)
         rstNeu.MoveNext
     Loop
End Sub

Listing 3: Die Prozedur TabellenAbgleichen

Schließlich durchlaufen wir das Recordset rstAlt in einer Do While-Schleife, bis die Datensatzmarkierung des auf EOF steht. In dieser Schleife durchlaufen wir auf bestimmte Weise beide Recordsets, wobei gegebenenfalls noch ein oder mehrere Datensätze im Recordset rstNeu übrig bleiben.

Dabei prüfen wir zunächst, ob rstNeu.EOF wahr ist. Das ist zu Beginn in der Regel noch nicht der Fall – außer, die neue Datenbank enthält gar keine Tabellen. Anderenfalls prüfen wir, ob der aktuelle Datensatz von rstAlt und rstNeu 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 MoveNext zur nächsten Position. Ist rstAlt.Fields(0) > rstNeu.Fields(0), dann stimmen die aktuellen Tabellennamen nicht überein und der Tabellenname aus rstNeu liegt im Alphabet vor dem aus rstAlt. In diesem Fall geben wir eine Zeichenkette aus vier Minuszeichen (—-) aus und den Namen der Tabelle aus rstNeu. Außerdem verschieben wir den Datensatzzeiger von rstNeu um eine Position weiter.

Das heißt, wir bleiben bei dem Tabellennamen von rstAlt und wechseln zum nächsten Eintrag von rstNeu. Die nächst ElseIf-Bedingung behandelt den umgekehrten Fall, nämlich dass der Eintrag aus rstAlt im Alphabet vor dem Eintrag aus rstNeu liegt. Dann geben wir den aus rstAlt aus und vier Minuszeichen statt des Eintrags aus rstNeu und verschieben den Datensatzzeiger in rstAlt um eine Position weiter nach hinten.

Wenn also etwa in beiden Datenbanken tblA und tblB vorhanden sind, werden diese so ausgegeben:

tblA tblA
tblB tblB

Wenn in tblA nur in rstNeu vorhanden ist, erhalten wir dieses Ergebnis:

---- tblA
tblB tblB

Ist tblA in beiden Datenbanken vorhanden und tblB nur in der alten, sieht das Ergebnis so aus:

tblA tblA
tblB ----

Nun gibt es drei Fälle für das Ende des Durchlaufens der Datensätze mit den Tabellenamen in der ersten Do While-Schleife:

  • Die letzte Tabelle ist in beiden Datenbanken enthalten. Dann wird die erste Do While-Schleife mit dem If-Teil der inneren If…Then-Bedingung verlassen, weil NOT rstAlt.EOF danach nicht mehr erfüllt ist. rstNeu.EOF ist dann auch True.
  • Die letzte Tabelle beziehungsweise die letzten Tabellen sind nur in rstAlt enthalten. Dann ist die äußere If…Then-Schleife False und der Else-Teil wird ausgeführt, wo der Datensatzzeiger in rstAlt solange weitergeschoben wird, bis die Do While-Bedingung rstAlt.EOF erfüllt ist.
  • Die letzte Tabelle beziehungsweise die letzten Tabellen sind nur in rstNeu enthalten. Dann wird die erste Do While-Schleife verlassen, nachdem der Datensatzzeiger für rstAlt auf EOF verschoben wird.

In allen Fällen wird die Bedingung der zweiten Do While-Schleife noch einmal geprüft, nämlich Not rstNeu.EOF. Sollten also nach dem Durchlaufen von rstAlt noch Datensätze in rstNeu vorhanden sein, durchläuft die Prozedur diese in der zweiten Do While-Schleife. Dort gibt sie für die übrigen Tabellen der neuen Version der Datenbank die Tabellennamen aus, also etwa so:

...
---- tblY
---- tblZ

Interpretation des Ergebnisses

Damit erhalten wir nun eine Gegenüberstellung der Tabellen der alten und der neuen Version der Datenbank, die etwa so aussehen könnte:

tblA tblA
tblB tblB
---- tblC
tblD ----
...
tblY tblY
tblZ tblZ

Die Tabelle tblC kommt nicht in der alten Datenbank vor, aber in der neuen Version. tblD hingegen kommt nur in der alten, aber nicht in der neuen Version vor. Alle anderen Tabellen sind in beiden Versionen der Datenbank vorhanden.

Die Interpretation könnte wie folgt lauten:

  • 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.
  • Eine Tabelle kommt in der alten Version vor, aber nicht mehr in der neuen und eine Umbenennung ist ausgeschlossen: Die Funktion, für die die Daten der Tabelle benö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ügt wurden, daher waren die entsprechenden Tabellen dort auch noch nicht enthalten. Sie können also so gegebenenfalls erkennen, dass Sie noch Plugins hinzufügen müssen, damit der neue Shop wie der alte funktioniert.
  • 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ötigt wird.

Felder vergleichen

Möchten Sie weiterlesen? Dann lösen Sie Ihr Ticket!
Hier geht es zur Bestellung des Jahresabonnements des Magazins Access im Unternehmen:
Zur Bestellung ...
Danach greifen Sie sofort auf alle rund 1.000 Artikel unseres Angebots zu - auch auf diesen hier!
Oder haben Sie bereits Zugangsdaten? Dann loggen Sie sich gleich hier ein:

Schreibe einen Kommentar