Access
 sql >> Base de données >  >> RDS >> Access

Insertions ou mises à jour en masse pour les tableaux avec des champs de pièce jointe

Depuis Access 2010, Access prend en charge le type de données Pièces jointes qui, à première vue, semble être une fonctionnalité pratique pour stocker de petites images ou des fichiers. Cependant, une recherche rapide sur Google montrera généralement qu'il vaut mieux les éviter. Tout cela se résume au fait qu'un type de données Pièces jointes est en fait un champ à valeurs multiples (MVF), et ceux-ci posent plusieurs problèmes. D'une part, vous seriez incapable d'utiliser des requêtes pour insérer ou mettre à jour plusieurs enregistrements en une seule fois. En effet, toutes les tables qui contiennent ce type de données vous obligent à faire beaucoup de code et pour cette seule raison, nous évitons d'utiliser ces types de données normalement.

Cependant, il y a un problème. Nous adorons utiliser la galerie d'images et les thèmes, qui dépendent tous deux d'une table système, MSysResources qui utilise malheureusement les types de données de pièce jointe. Cela a créé un problème pour la gestion des ressources dans notre bibliothèque standard car nous voulons utiliser les MSysResources mais nous ne pouvons pas facilement les mettre à jour ou les insérer en masse.

Le type de données de pièce jointe (ainsi que les MVF) vous oblige à utiliser la programmation "ligne par ligne agonisante" lorsqu'il s'agit d'un champ MVF, c'est un double avec le champ Pièces jointes car vous devrez utiliser le LoadFromFile ou SaveToFile méthodes. Microsoft a un article avec des exemples sur ces méthodes. Ainsi, vous devez interagir avec le système de fichiers lors de l'ajout de nouveaux enregistrements. Pas toujours souhaitable dans toutes les situations. Maintenant, si nous copions d'une table à une autre, nous pouvons éviter de rebondir sur le système de fichiers en faisant quelque chose comme :

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

Sacré boucle, Batman ! C'est beaucoup de code, tout simplement pour copier des pièces jointes d'une table à une autre. Même si nous ne rebondissons pas sur le système de fichiers, il est également très lent. D'après notre expérience, une table avec 1 000 enregistrements contenant une seule pièce jointe peut prendre minutes juste pour traiter. Maintenant, c'est assez démesuré quand on considère la taille. La table avec les pièces jointes n'est pas si grande. En fait, faisons une expérience. Voyons ce qui se passe si je copie et colle via la fiche technique :

Ainsi, le copier-coller est pratiquement instantané. Évidemment, le code utilisé par collage n'est pas le même code que nous utiliserions dans VBA. Cependant, nous croyons fermement que si nous pouvons le faire de manière interactive, nous pouvons également le faire en VBA. Pouvons-nous reproduire la vitesse du collage interactif dans VBA ? La réponse s'avère être oui, nous le pouvons !

Accélérez avec …. XML ?

Étonnamment, la méthode qui offre le moyen le plus rapide de copier des données, y compris des pièces jointes, consiste à utiliser des fichiers XML. J'admets que je n'utilise pas les fichiers XML, sauf pour contourner les limitations. En moyenne, les fichiers XML sont relativement lents par rapport aux autres formats de fichiers, mais dans ce cas, XML présente un énorme avantage ; il n'a aucun problème à décrire les MVF. Créons un fichier XML et étudions les capacités que nous obtenons avec l'importation/exportation d'un fichier XML.

Après la boîte de dialogue habituelle de l'assistant d'exportation pour définir le chemin d'enregistrement du fichier XML, nous aurons une boîte de dialogue comme celle-ci :

Si nous cliquons ensuite sur le bouton "Plus d'options…", nous obtenons cette boîte de dialogue à la place :

À partir de cette boîte de dialogue, nous voyons quelques indices supplémentaires sur ce qui est possible ; à savoir :

  • Nous avons la possibilité d'exporter l'intégralité du tableau ou seulement un sous-ensemble du tableau en appliquant un filtre
  • Nous pouvons transformer la sortie XML.
  • Nous pouvons décrire le schéma en plus du contenu du tableau.

Je trouve qu'il est préférable d'intégrer le schéma ; la valeur par défaut est de l'exporter mais dans un fichier séparé. Cependant, cela peut être source d'erreurs et ils peuvent oublier d'inclure le fichier XSD avec le fichier XML. Cela peut être modifié via l'onglet schéma affiché :

Finissons de l'exporter et examinons rapidement les données du fichier XML résultant.

Notez que les pièces jointes sont décrites dans les Data le contenu de la sous-arborescence et du fichier est encodé en base 64. Essayons d'importer le fichier XML. Après avoir parcouru l'assistant d'importation, nous obtiendrons cette boîte de dialogue :

Prenez note des fonctionnalités suivantes :

  • Comme pour l'exportation, nous avons la possibilité de transformer le XML.
  • Nous pouvons contrôler s'il faut importer la structure, les données ou les deux

Si nous finissons ensuite d'importer le fichier XML, nous constatons que c'est aussi rapide que l'opération de copier-coller que nous avons faite.

Nous savons maintenant qu'il existe un meilleur moyen de copier plusieurs enregistrements avec des pièces jointes. Mais dans cette situation, nous voulons le faire par programmation plutôt qu'interactivement. Pouvons-nous faire la même chose que nous venons de faire ? A nouveau la réponse est oui. Il y a plusieurs façons de faire la même chose mais je pense que la méthode la plus simple est d'utiliser les 3 nouvelles méthodes qui ont été ajoutées à l'Application objet depuis Access 2010 :

  • ExportXML méthode
  • TransformXML méthode
  • ImportXML méthode

Notez que le ExportXML prend en charge l'exportation à partir de divers objets. Cependant, comme l'objectif ici est de pouvoir copier ou mettre à jour en masse les enregistrements d'une table avec des champs de pièce jointe, le meilleur type d'objet que nous pouvons utiliser est une requête enregistrée. Avec une requête enregistrée, nous pouvons contrôler quelles lignes doivent être insérées ou mises à jour et nous pouvons également façonner la sortie. Si vous regardez la conception du schéma de MSysResources tableau ci-dessous :

Il y a un problème potentiel. Chaque fois que nous utilisons des thèmes ou des images, nous référençons l'élément par son nom, et non par son identifiant. Cependant, le Name colonne n'est pas unique et n'est pas la clé primaire de la table. Par conséquent, lorsque nous ajoutons ou mettons à jour des enregistrements, nous voulons faire correspondre le Name colonne, pas l'Id colonne. Cela signifie que lorsque nous exportons, nous ne devrions probablement pas inclure le Id colonne et nous devrions exporter uniquement la liste unique du Name pour s'assurer que les ressources ne passent pas soudainement de "Open.png" à "Close.png" ou quelque chose de stupide.

Nous allons ensuite créer une requête pour servir de source pour les enregistrements que nous voulons importer dans le MSysResources table. Commençons par ce SQL juste pour démontrer le filtrage vers un sous-ensemble d'enregistrements :

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

Nous l'enregistrerons ensuite sous qryResourcesExport . On peut alors écrire du code VBA pour exporter du XML :

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

Cela émule l'exportation que nous avons initialement effectuée de manière interactive.

Cependant, si nous importons ensuite le XML résultant, nous n'avons que la possibilité d'ajouter des données à une table existante. Nous ne pouvons pas contrôler dans quelle table il sera ajouté ; il trouvera une table ou une table de requête du même nom (par exemple, qryResourcesExport et ajouter des enregistrements dans cette requête. Si la requête peut être mise à jour, alors il n'y a pas de problème et elle sera insérée dans l'Example sur lequel la requête est basée. Mais que se passe-t-il si la requête source que nous utilisons ne peut pas être mise à jour ou n'existe pas ? Dans les deux cas, nous ne serions pas en mesure d'importer le fichier XML tel quel. L'importation peut échouer ou créer une nouvelle table nommée qryResourcesExport qui ne nous aide pas. Et qu'en est-il du cas de la copie de données depuis Example vers MSysResources ? Nous ne voulons pas ajouter de données à l'Example tableau.

C'est là que le TransformXML méthode vient à la rescousse. Une discussion complète sur la façon d'écrire une transformation XML dépasse le cadre, mais vous devriez pouvoir trouver de nombreuses ressources sur la façon d'écrire une feuille de style XSLT pour décrire la transformation. Il existe plusieurs outils en ligne que vous pouvez également utiliser pour valider votre XSLT. En voici un. Pour le cas simple où nous voulons simplement contrôler dans quelle table le fichier XML doit ajouter les enregistrements, vous pouvez commencer avec ce fichier XSLT. Vous pouvez ensuite exécuter le code VBA suivant :

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

Nous pouvons remplacer le fichier XML d'origine par le fichier XML transformé, qui va maintenant s'insérer dans le MSysResources table plutôt que dans (peut-être une requête/table inexistante) qryResourcesExport .

Nous devons ensuite gérer les mises à jour. Parce que nous ajoutons en fait de nouveaux enregistrements, et le MSysResources table n'a aucune contrainte sur les noms en double, nous devons nous assurer que tous les enregistrements existants portant le même nom sont d'abord supprimés. Cela peut être accompli en écrivant une requête équivalente comme ceci :

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

puis l'exécuter avant d'exécuter le code VBA :

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

Parce que le fichier XML a été transformé, le ImportXML La méthode va maintenant insérer les données dans le MSysResources table plutôt que la requête d'origine que nous avons utilisée avec le ExportXML méthode. Nous spécifions qu'il doit ajouter des données dans une table existante. Cependant, si la table n'existe pas, elle sera créée.

Et avec cela, nous avons réalisé une mise à jour/insertion en masse de la table avec un champ de pièce jointe qui est beaucoup plus rapide par rapport au code VBA original du jeu d'enregistrements et du jeu d'enregistrements enfant. J'espère que cela pourra aider! De plus, si vous avez besoin d'aide pour développer des applications Access, n'hésitez pas à nous contacter !