Access
 sql >> Datenbank >  >> RDS >> Access

Masseneinfügungen oder -aktualisierungen für Tabellen mit Anhangsfeldern

Seit Access 2010 unterstützt Access den Datentyp Attachments, der oberflächlich betrachtet eine praktische Funktion zum Speichern kleiner Bilder oder Dateien zu sein scheint. Eine schnelle Google-Suche zeigt jedoch normalerweise, dass sie am besten vermieden werden. Dies alles läuft darauf hinaus, dass ein Anhangsdatentyp eigentlich ein mehrwertiges Feld (MVF) ist, und diese bringen mehrere Probleme mit sich. Zum einen könnten Sie Abfragen nicht verwenden, um mehrere Datensätze auf einmal einzufügen oder zu aktualisieren. Tatsächlich zwingen Sie alle Tabellen, die einen solchen Datentyp enthalten, dazu, viel Code zu schreiben, und allein aus diesem Grund vermeiden wir normalerweise die Verwendung solcher Datentypen.

Es gibt jedoch ein Problem. Wir lieben es, die Bildergalerie und Themen zu verwenden, die beide von einer Systemtabelle, MSysResources, abhängen die leider die Anhangsdatentypen verwendet. Dies hat zu einem Problem bei der Ressourcenverwaltung in unserer Standardbibliothek geführt, da wir die MSysResources verwenden möchten aber wir können sie nicht einfach in großen Mengen aktualisieren oder einfügen.

Der Attachment-Datentyp (sowie MVFs) zwingt Sie dazu, eine „row-by-agonizing-row“-Programmierung zu verwenden, wenn Sie mit einem MVF-Feld arbeiten, es ist ein Twofer mit dem Attachments-Feld, da Sie das LoadFromFile oder SaveToFile Methoden. Microsoft hat einen Artikel mit Beispielen zu diesen Methoden. Daher müssen Sie beim Hinzufügen neuer Datensätze mit dem Dateisystem interagieren. Nicht immer in allen Situationen wünschenswert. Wenn wir nun von einer Tabelle in eine andere Tabelle kopieren, können wir das Springen über das Dateisystem vermeiden, indem wir Folgendes tun:

Dim SourceParentRs As DAO.Recordset2
Dim SourceChildRs As DAO.Recordset2
Dim TargetParentRs As DAO.Recordset2
Dim TargetChildRs As DAO.Recordset2
Dim SourceField As DAO.Field2

Set SourceParentRs = db.OpenRecordset("TableWithAttachmentField", dbOpenDynaset)
Set TargetParentRs = db.OpenRecordset("AnotherTableWithAttachmentField", dbOpenDynaset, dbAppendOnly)

Do Until SourceParentRs.EOF
  TargetParentRs.AddNew
  For Each SourceField In SourceParentRs.Fields
    If SourceField.Type <> dbAttachment Then
      TargetParentRs.Fields(SourceField.Name).Value = SourceField.Value
    End If
  Next

  TargetParentRs.Update 'Must save record first before can edit MVF fields
  TargetParentRs.Bookmark = TargetParentRs.LastModified
  Set SourceChildRs = SourceParentRs.Fields("Data").Value
  Set TargetChildRs = TargetParentRs.Fields("Data").Value
  Do Until SourcechildRs.EOF
    TargetChildRs.AddNew
    Const ChunkSize As Long = 32768
    Dim TotalSize As Long
    Dim Offset As Long

    TotalSize = SourceChildRs.Fields("FileData").FieldSize
    Offset = TotalSize Mod ChunkSize
    TargetChildRs.Fields("FileData").AppendChunk(SourceChildRs.GetChunk(0, Offset)
    Do Until Offset > TotalSize
      TargetChildRs.Fields("FileData").AppendChunk(SourceChildRs.GetChunk(Offset, ChunkSize)
      Offset = Offset + ChunkSize
    Loop
    TargetChildRs.Update
    SourceChildRs.MoveNext
  Loop
  TargetParentRs.Update
  SourceParentRs.MoveNext
Loop

Heilige Schleife, Batman! Das ist eine Menge Code, alles nur, um Anhänge von einer Tabelle in eine andere zu kopieren. Auch wenn wir nicht über das Dateisystem hüpfen, ist es auch sehr langsam. Unserer Erfahrung nach kann eine Tabelle mit 1000 Datensätzen, die einen einzigen Anhang enthalten, Minuten dauern nur zu verarbeiten. Nun, das ist ziemlich überdimensioniert, wenn man die Größe bedenkt. Der Tisch mit den Anhängen ist nicht so groß. Machen wir tatsächlich ein Experiment. Mal sehen, was passiert, wenn ich per Datenblatt kopiere und einfüge:

Das Kopieren und Einfügen erfolgt also praktisch augenblicklich. Offensichtlich ist der beim Einfügen verwendete Code nicht derselbe Code, den wir in VBA verwenden würden. Wir sind jedoch fest davon überzeugt, dass wir es auch in VBA tun können, wenn wir es interaktiv tun können. Können wir die Geschwindigkeit des interaktiven Einfügens in VBA replizieren? Die Antwort lautet ja, wir können!

Beschleunigen mit …. XML?

Überraschenderweise ist die Methode, die den schnellsten Weg zum Kopieren von Daten, einschließlich Anhängen, bietet, über XML-Dateien. Ich gebe zu, dass ich nicht nach XML-Dateien greife, außer als Problemumgehung für Einschränkungen. Im Durchschnitt sind XML-Dateien relativ langsam gegenüber anderen Dateiformaten, aber in diesem Fall hat XML einen großen Vorteil; es hat kein Problem damit, MVFs zu beschreiben. Lassen Sie uns eine XML-Datei erstellen und die Möglichkeiten untersuchen, die wir durch das Importieren/Exportieren einer XML-Datei erhalten.

Nach dem üblichen Dialogfeld des Exportassistenten zum Festlegen des Pfads zum Speichern der XML-Datei erhalten wir ein Dialogfeld wie dieses:

Wenn wir dann auf die Schaltfläche „Weitere Optionen…“ klicken, erhalten wir stattdessen diesen Dialog:

Aus diesem Dialog sehen wir einige weitere Hinweise darauf, was möglich ist; nämlich:

  • Wir haben die Möglichkeit, die gesamte Tabelle oder nur einen Teil der Tabelle zu exportieren, indem wir einen Filter anwenden
  • Wir können die XML-Ausgabe transformieren.
  • Wir können das Schema zusätzlich zum Inhalt der Tabelle beschreiben.

Ich finde, dass es am besten ist, das Schema einzubetten; Standardmäßig wird es exportiert, jedoch als separate Datei. Das kann jedoch fehleranfällig sein und sie können vergessen, die XSD-Datei mit der XML-Datei einzuschließen. Dies kann über den angezeigten Schema-Tab geändert werden:

Lassen Sie uns den Export beenden und einen kurzen Blick auf die Daten der resultierenden XML-Datei werfen.

Beachten Sie, dass die Anhänge innerhalb der Daten beschrieben werden Teilbaum- und Dateiinhalte sind Base-64-kodiert. Versuchen wir, die XML-Datei zu importieren. Nachdem wir den Importassistenten durchlaufen haben, erhalten wir diesen Dialog:

Beachten Sie die folgenden Funktionen:

  • Wie beim Export haben wir die Möglichkeit, das XML zu transformieren.
  • Wir können steuern, ob die Struktur, die Daten oder beide importiert werden sollen

Wenn wir dann den Import der XML-Datei abschließen, stellen wir fest, dass es genauso schnell geht wie das Kopieren und Einfügen, das wir durchgeführt haben.

Wir wissen jetzt, dass es einen besseren Weg gibt, mehrere Datensätze mit Anhängen zu kopieren. Aber in dieser Situation möchten wir dies eher programmgesteuert als interaktiv tun. Können wir dasselbe tun, was wir gerade getan haben? Auch hier lautet die Antwort ja. Es gibt mehrere Möglichkeiten, dasselbe zu tun, aber ich denke, die einfachste Methode ist die Verwendung der 3 neuen Methoden, die der Application hinzugefügt wurden Objekt seit Access 2010:

  • XML exportieren Methode
  • TransformXML Methode
  • XML importieren Methode

Beachten Sie, dass die Datei ExportXML -Methode unterstützt den Export aus verschiedenen Objekten. Da das Ziel hier jedoch darin besteht, die Datensätze einer Tabelle mit angehängten Feldern massenhaft kopieren oder aktualisieren zu können, ist der beste Objekttyp, den wir verwenden, eine gespeicherte Abfrage. Mit einer gespeicherten Abfrage können wir steuern, welche Zeilen eingefügt oder aktualisiert werden sollen, und wir können auch die Ausgabe gestalten. Betrachtet man das Schemadesign der MSysResources Tabelle unten:

Es gibt ein potenzielles Problem. Wann immer wir Themen oder Bilder verwenden, verweisen wir auf den Artikel mit dem Namen, nicht mit der ID. Allerdings ist der Name Spalte ist nicht eindeutig und nicht der Primärschlüssel der Tabelle. Wenn wir Datensätze hinzufügen oder aktualisieren, möchten wir daher den Namen abgleichen Spalte, nicht die Id Säule. Das bedeutet, dass wir beim Exportieren die Id wahrscheinlich nicht angeben sollten Spalte und wir sollten nur die eindeutige Liste des Namen exportieren um sicherzustellen, dass die Ressourcen nicht plötzlich von „Open.png“ zu „Close.png“ oder etwas Dummem wechseln.

Anschließend erstellen wir eine Abfrage, die als Quelle für die Datensätze fungiert, die wir in die MSysResources importieren möchten Tisch. Beginnen wir mit dieser SQL, nur um das Herunterfiltern auf eine Teilmenge von Datensätzen zu demonstrieren:

SELECT e.Data, e.Extension, e.Name, e.Type
FROM Example AS e
WHERE e.Name In ("blue","red","green");

Wir speichern es dann als qryResourcesExport . Wir können dann VBA-Code schreiben, um XML zu exportieren:

Application.ExportXML _
  ObjectType:=acExportQuery, _
  DataSource:="qryResourcesExport", _
  DataTarget:="C:\Path\to\Resources.xml", _
  OtherFlags:=acEmbedSchema

Dies emuliert den Export, den wir ursprünglich interaktiv durchgeführt haben.

Wenn wir dann aber das resultierende XML importieren, haben wir nur die Möglichkeit, Daten an eine bestehende Tabelle anzuhängen. Wir können nicht steuern, an welche Tabelle es angehängt wird; Es findet eine Tabelle oder Abfragetabelle mit demselben Namen (z. B. qryResourcesExport und Datensätze an diese Abfrage anhängen. Wenn die Abfrage aktualisierbar ist, gibt es kein Problem und sie wird in das Example eingefügt auf der die Abfrage basiert. Was aber, wenn die von uns verwendete Quellabfrage nicht aktualisierbar ist oder möglicherweise nicht existiert? In beiden Fällen können wir die XML-Datei nicht unverändert importieren. Der Import könnte entweder fehlschlagen oder am Ende eine neue Tabelle mit dem Namen qryResourcesExport erstellen was uns nicht hilft. Und was ist mit dem Kopieren von Daten aus Example zu MSysResources ? Wir möchten keine Daten an das Beispiel anhängen Tabelle.

Dort befindet sich die TransformXML Methode kommt zur Rettung. Eine vollständige Diskussion darüber, wie man eine XML-Transformation schreibt, würde den Rahmen sprengen, aber Sie sollten in der Lage sein, reichlich Ressourcen zum Schreiben eines XSLT-Stylesheets zu finden, um die Transformation zu beschreiben. Es gibt mehrere Online-Tools, die Sie auch zur Validierung Ihres XSLT verwenden können. Hier ist eins. Für den einfachen Fall, in dem wir nur steuern möchten, an welche Tabelle die XML-Datei die Datensätze anhängen soll, können Sie mit dieser XSLT-Datei beginnen. Sie können dann den folgenden VBA-Code ausführen:

Application.TransformXML _
  DataSource:="C:\Path\to\Resources.xml", _
  TransformSource:="C:\Path\to\ResourcesTransform.xslt", _
  OutputTarget:="C:\Path\to\Resources.xml", _
  WellFormedXMLOutput:=True, _
  ScriptOption:=acEnableScript

Wir können die ursprüngliche XML-Datei durch die transformierte XML-Datei ersetzen, die nun in die MSysResources eingefügt wird Tabelle statt in (möglicherweise nicht existierende Abfrage/Tabelle) qryResourcesExport .

Wir müssen uns dann um die Updates kümmern. Weil wir tatsächlich neue Datensätze anhängen und die MSysResources Tabelle keine Einschränkungen für doppelte Namen hat, müssen wir sicherstellen, dass alle vorhandenen Datensätze mit denselben Namen zuerst gelöscht werden. Dies kann erreicht werden, indem eine äquivalente Abfrage wie folgt geschrieben wird:

DELETE FROM MSysResources AS r
WHERE r.Name In ("blue","red","green");

dann führen Sie es zuerst aus, bevor Sie den VBA-Code ausführen:

Application.ImportXML DataSource:="C:\Path\to\Resources.xml", ImportOptions:=acAppendData

Da die XML-Datei transformiert wurde, wird die Datei ImportXML -Methode fügt nun die Daten in die MSysResources ein Tabelle anstelle der ursprünglichen Abfrage, die wir mit ExportXML verwendet haben Methode. Wir geben an, dass Daten an eine vorhandene Tabelle angehängt werden sollen. Wenn die Tabelle jedoch nicht existiert, wird sie erstellt.

Und damit haben wir eine Massenaktualisierung/-einfügung der Tabelle mit einem Anhangsfeld erreicht, die im Vergleich zum ursprünglichen VBA-Code mit Recordset und untergeordnetem Recordset viel schneller ist. Ich hoffe, das hilft! Auch wenn Sie Hilfe bei der Entwicklung von Access-Anwendungen benötigen, können Sie sich gerne an uns wenden!