Im Beitrag „Anlage-Feld zum SQL Server migrieren“ (www.access-im-unternehmen.de/1542) haben wir gezeigt, wir man die Inhalte von Anlagefeldern einer Access-Datenbank in ein varbinary(max)-Feld einer SQL Server-Datenbank übertragen können. Dort sind wie davon ausgegangen, dass jedes Anlagefeld immer nur eine Datei enthält, was in vielen Fällen ausreichend ist. Was aber, wenn der Entwickler das Anlagefeld für mehrere Dateien vorgesehen hat – beispielsweise, um ein oder mehrere Produktbilder zu einem Produkt zu speichern? In diesem Fall müssen wir umdenken, den wir können optimalerweise immer nur eine Datei in einem varbinary(max)-Feld speichern. Zum Speichern mehrerer Dateien müssen wir uns also einen Workaround überlegen. Wie dieser aussieht, schauen wir uns im vorliegenden Beitrag an.
Anlagefelder mit einem oder mehreren Dateien
Anlagefelder speichern ihre Anlagen in einer internen Tabelle, die von außen erst einmal nicht sichtbar ist. Deshalb ist es auch möglich, dass für einen einzelnen Datensatz mehr als eine Anlage hinterlegt werden können.
Im Beispiel verwenden wir die Tabelle tblProdukte, die in der Entwurfsansicht wie in Bild 1 aussieht.
Bild 1: Tabelle zum Speichern von Produkten mit Bildern
In der Datenblattansicht haben wir der Tabelle drei Bilder zum Anlagefeld hinzugefügt (siehe Bild 2).
Bild 2: Tabelle mit mehreren Anlagen
Dass es in einem Anlagefeld ein Objekt wie eine Tabelle geben muss, sehen wir, wenn wir eine Tabelle mit einem Anlagefeld in eine neue, leere Abfrage ziehen. Hier fügen wir einmal alle Felder der Tabelle einschließlich der Felder des Anlagefeldes zum Entwurfsraster hinzu (siehe Bild 3).
Bild 3: Die Untertabelle in einem Anlagefeld
Wechseln wir hier in die Datenblattansicht, sehen wir, dass die Idee, dass wir es hier mit einer internen Untertabelle zu tun haben, nicht allzu abwegig ist. Aus dem einen Datensatz werden nun direkt drei Datensätze, was sonst nur geschieht, wenn wir eine verknüpfte Tabelle mit in den Abfrageentwurf ziehen (siehe Bild 4).
Bild 4: Die Felder eines Anlagefeldes in der Datenblattansicht
Anlagen mit mehreren Dateien zum SQL Server migrieren
Betrachten wir nun die Aufgabe, die Inhalte einer solchen in eine SQL Server-Datenbank zu übertragen, sehen wir erst einmal kein derartiges Konstrukt, indem wir mehrere Datensätze pro Feld speichern könnten.
Letztlich ist das, was wir hier abgebildet sehen, nichts anderes als zwei Tabellen, die per 1:n-Beziehung miteinander verknüpft sind. Und das lässt sich wiederum auch im SQL Server abbilden – wenn auch mit Unterstützung durch eine weitere Tabelle.
Wir erstellen also im SQL Server zwei Tabellen, um die Daten dieser einen Tabelle mit Anlagefeld abzubilden. Diese sehen wie folgt aus:
- tblProdukte: ProduktID (Primärschüsselfeld), Produkt (Textfeld)
- tblProduktbilder: ProduktbildID (Primärschlüsselfeld), ProduktID (Fremdschlüsselfeld zur Tabelle tblProduktbilder), Produktbild (Datentyp varbinary(max))
Um die Tabellen schnell zu erstellen, können wir das folgende Skript nutzen:
CREATE TABLE tblProdukte ( ProduktID INT IDENTITY(1,1) PRIMARY KEY, Produkt NVARCHAR(255) NOT NULL);CREATE TABLE tblProduktbilder ( ProduktbildID INT IDENTITY(1,1) PRIMARY KEY, ProduktID INT NOT NULL, Produktbild VARBINARY(MAX) NOT NULL, CONSTRAINT FK_Produktbilder_Produkte FOREIGN KEY (ProduktID) REFERENCES tblProdukte (ProduktID) ON DELETE CASCADE);
Danach können wir uns an die Erstellung des Code zum Übertragen der Daten samt Bilddaten begeben.
Notwendiger Parameter zum Übertragen der Tabelle inklusive Anlagefeld
Im Beitrag Anlage-Feld zum SQL Server migrieren (www.access-im-unternehmen.de/1542) haben wir bereits eine ähnliche Funktion programmiert, die wir komplett über Parameter steuern können. Im vorliegenden Beitrag benötigen wie eine ähnliche Prozedur, die allerdings noch einige weitere Parameter benötigt. Immerhin sollen die Daten aus ursprünglich einer Tabelle nun auf zwei Tabellen aufgeteilt werden.
Zuvor legen wir fest, wie wir hier vorgehen wollen. Wollen wir eine ähnliche Prozedur schreiben, wie wir sie im oben genannten Beitrag angelegt haben, die für eine bereits bestehende Migration inklusive Daten nur noch die Inhalte der Anlagefelder hinterherkopiert? Dies ist in den Fällen interessant, wo man die übrige Migration bereits vollständig durchgeführt hat, beispielsweise mit einem Tool wie SQL Server Migration Assistant.
In diesem Beitrag wollen wir jedoch eine Lösung kreieren, welche die Inhalte der Tabellen vollständig überträgt.
Wir gehen also von den leeren Tabellen tblProdukte und tblProduktbilder auf dem SQL Server aus und wollen alle enthaltenen Informationen dorthin übertragen.
Wir werden also in den folgenden Schritten vorgehen:
- Übertragen eines Datensatzes der Tabelle tblProdukte zur gleichnamigen SQL Server-Tabelle (mit Ausnahme des Anlagefeldes)
- Merken des neuen Primärschlüsselwertes
- Durchlaufen der Anlagen und schrittweises Übertragen der Anlagen in die Tabelle tblProdukteBilder, wobei wir den gemerkten Primärschlüsselwert als Fremdschlüsselwert nutzen
Für die Parametrisierung benötigen wir also die folgenden Daten:
- strAccessTable: Name der Haupttabelle
- strAccessField: Name des Anlagefeldes der Haupttabelle
- strAccessPKField: Name des Primärschlüsselfeldes der Haupttabelle
- lngAccessPKID: Wert des Primärschlüsselfeldes des aktuellen Datensatzes
- strSQLTable: Name der Haupttabelle im SQL Server
- strSQLPKField: Name des Primärschlüsselfeldes der Haupttabelle im SQL Server
- strSQLAttachmentTable: Name der Anlagentabelle im SQL Server
- strSQLAttachmentPKField: Name des Primärschlüsselfeldes der Anlagentabelle im SQL Server
- strSQLVarBinaryMaxField: Name des varbinary(max)-Feldes in der Anlagentabelle im SQL Server
- strSQLAttachmentFKField: Name des Fremdschlüsselfeldes der Anlagentabelle im SQL Server
- cnn: Verbindung zum SQL Server
- db: Verweis auf das aktuelle Database-Objekt
- strErrorMessage: Rückgabeparameter für Fehlermeldungen
Aufruf der Funktionen
Den Start machen wir mit der Prozedur TabelleMitMehrfachanlagenZumSQLServer (siehe Listing 1). Diese erstellt einen Verweis auf das aktuelle Database-Objekt sowie eine Connection auf die entsprechende SQL Server-Datenbank.
Public Sub TabelleMitMehrfachanlagenZumSQLServer() Dim db As DAO.Database Dim cnn As ADODB.Connection Dim rst As DAO.Recordset Dim strErrorMessage As String Dim strSQL As String Dim cmd As ADODB.Command Dim rstResult As ADODB.Recordset Dim lngID As Long Dim bolKopiert As Boolean Set db = CurrentDb Set cnn = New ADODB.Connection cnn.Open cStrConnection Set rst = db.OpenRecordset("SELECT * FROM tblProdukte", dbOpenDynaset) Do While Not rst.EOF strSQL = "INSERT INTO tblProdukte(Produkt) OUTPUT INSERTED.ProduktID VALUES(''" & rst!Produkt & "'');" Set cmd = New ADODB.Command cmd.ActiveConnection = cnn cmd.CommandText = strSQL cmd.CommandType = adCmdText Set rstResult = cmd.Execute() If Not rstResult.EOF Then lngID = rstResult!ProduktID bolKopiert = CopyAttachmentToVarBinaryMax_Multi("tblProdukte", "Produktbilder", "ProduktID", _ rst!ProduktID, lngID, "tblProduktbilder", "Produktbild", "ProduktID", cnn, db, strErrorMessage) If bolKopiert = True Then MsgBox "Datensatz " & rst!ProduktID & " erfolgreich kopiert." Else MsgBox "Datensatz " & rst!ProduktID & " nicht kopiert." & vbCrLf & vbCrLf & strErrorMessage, _ vbCritical + vbOKOnly, "Fehler beim Kopieren" End If End If rst.MoveNext Loop rst.Close Set rst = Nothing Set db = Nothing cnn.Close Set cnn = Nothing End Sub
Listing 1: Aufruf der Funktionen zum Kopieren von Tabellen mit Anlagefeldern mit mehreren Dateien
Die dafür notwendige Verbindungszeichenfolge haben wir in diesem Fall wie folgt in einer Konstanten hinterlegt:
Public Const cStrConnection As String = "Provider=MSOLEDBSQL;Data Source=amvdesktop2023;Initial Catalog=Anlagen;Integrated Security=SSPI;"
Die Verbindung öffnet die Prozedur dann mit der Open-Methode. Außerdem füllen wir ein Recordset mit den Datensätze der Tabelle tblProdukte und durchlaufen dieses in einer Do While-Schleife über alle Datensätze.
Darin kopieren wir zunächst die Daten dieser Tabelle mit Ausnahme des Anlagefeldes. Dazu erstellen wir eine entsprechende INSERT INTO-Anweisung. Diese wollen wir mithilfe eines Command-Objekts ausführen, dem wir die Verbindung aus cnn zuweisen und für das wir die INSERT INTO-Anweisung als CommandText hinterlegen.
Die INSERT INTO-Anweisung hat die Besonderheit, dass wir in ihr mit OUTPUT INSERTED.ProduktID direkt den Primärschlüsselwert des neu erstellten Datensatzes ermitteln. Um diesen später auszulesen, speichern wir das Ergebnis der Execute-Methode in einem Recordset namens rstResults.
Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...
den kompletten Artikel im PDF-Format mit Beispieldatenbank
diesen und alle anderen Artikel mit dem Jahresabo