sql >> Database teknologi >  >> RDS >> Access

Masseindsættelser eller opdatering for tabeller med vedhæftede felter

Siden Access 2010 har Access understøttet vedhæftede datatyper, som på overfladen virker som en praktisk funktion til lagring af små billeder eller filer. En hurtig google-søgning vil dog som regel vise, at de bedst undgås. Alt dette bunder i, at en Attachments-datatype faktisk er et Multi-Valued Field (MVF), og disse kommer med flere problemer. For det første ville du ikke være i stand til at bruge forespørgsler til at indsætte eller opdatere flere poster på én gang. Faktisk tvinger alle tabeller, der indeholder en sådan datatype, dig til at lave en masse kode, og alene af den grund undgår vi at bruge sådanne datatyper normalt.

Der er dog et problem. Vi elsker at bruge billedgalleriet og temaerne, som begge afhænger af en systemtabel, MSysResources som desværre bruger de vedhæftede datatyper. Dette har skabt et problem med at administrere ressourcer i vores standardbibliotek, fordi vi ønsker at bruge MSysResources men vi kan ikke nemt opdatere eller indsætte dem i bulk.

Vedhæftningsdatatypen (såvel som MVF'er) tvinger dig til at bruge "række-for-lidende-række"-programmering, når du har at gøre med et MVF-felt, det er en twofer med vedhæftede filer, fordi du skal bruge LoadFromFile eller SaveToFile metoder. Microsoft har en artikel med eksempler om disse metoder. Derfor skal du interagere med filsystemet, når du tilføjer nye poster. Ikke altid ønskværdigt i alle situationer. Nu, hvis vi kopierer fra en tabel til en anden tabel, kan vi undgå at hoppe over filsystemet ved at gøre noget som:

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

Hellige sløjfe, batman! Det er en masse kode, alt sammen bare for at kopiere vedhæftede filer fra en tabel til en anden. Selvom vi ikke hopper over filsystemet, er det også meget langsomt. Det er vores erfaring, at en tabel med 1000 poster, der indeholder en enkelt vedhæftet fil, kan tage minutter bare at bearbejde. Nu er dette ret overdimensioneret, når man tænker på størrelsen. Bordet med vedhæftede filer er ikke så stort. Faktisk, lad os lave et eksperiment. Lad os se, hvad der sker, hvis jeg kopierer og indsætter via dataark:

Så kopiering og indsættelse sker næsten øjeblikkeligt. Det er klart, at koden, der bruges til at indsætte, ikke er den samme kode, som vi ville bruge i VBA. Men vi er meget overbeviste om, at hvis vi kan gøre det interaktivt, kan vi også gøre det i VBA. Kan vi gentage hastigheden af ​​interaktiv indsættelse i VBA? Svaret viser sig at være ja, det kan vi!

Fremskynd med …. XML?

Overraskende nok er den metode, der giver den hurtigste måde at kopiere data på, inklusive vedhæftede filer, via XML-filer. Jeg vil indrømme, at jeg ikke rækker ud efter XML-filer undtagen som en løsning på begrænsninger. I gennemsnit er XML-filer relativt langsomme i forhold til andre filformater, men i dette tilfælde har XML en stor fordel; det har ingen problemer med at beskrive MVF'er. Lad os oprette en XML-fil og undersøge de muligheder, vi får ved at importere/eksportere en XML-fil.

Efter den sædvanlige eksportguide-dialog for at indstille stien til at gemme XML-filen, får vi en dialog som denne:

Hvis vi derefter klikker på knappen "Flere valgmuligheder...", får vi denne dialog i stedet:

Fra denne dialog ser vi få flere ledetråde om, hvad der er muligt; nemlig:

  • Vi har mulighed for at eksportere hele tabellen eller kun en delmængde af tabellen ved at anvende et filter
  • Vi kan transformere XML-outputtet.
  • Vi kan beskrive skemaet ud over indholdet af tabellen.

Jeg finder, at det er bedst at indlejre skemaet; standarden er at eksportere den, men som en separat fil. Det kan dog være udsat for fejl, og de kan glemme at inkludere XSD-filen med XML-filen. Dette kan ændres via skemafanen vist:

Lad os afslutte eksporten og tage et hurtigt kig på den resulterende XML-fils data.

Bemærk, at de vedhæftede filer er beskrevet i Data undertræ og filindhold er base-64-kodet. Lad os prøve at importere XML-filen. Efter at have gennemgået importguiden får vi denne dialogboks:

Bemærk følgende funktioner:

  • Som med eksport, har vi mulighed for at transformere XML.
  • Vi kan kontrollere, om strukturen, dataene eller begge dele skal importeres

Hvis vi derefter afslutter importen af ​​XML-filen, finder vi ud af, at den er lige så hurtig som den copy'n'paste-operation, vi gjorde.

Vi ved nu, at der er en bedre vej til at kopiere flere poster med vedhæftede filer. Men i denne situation ønsker vi at gøre dette programmatisk i stedet for interaktivt. Kan vi gøre det samme, som vi lige har gjort? Igen er svaret ja. Der er flere måder at gøre det samme på, men jeg tror, ​​at den nemmeste metode er at bruge de 3 nye metoder, der blev tilføjet til applikationen objekt siden Access 2010:

  • EksporterXML metode
  • TransformXML metode
  • ImporterXML metode

Bemærk, at ExportXML metoden understøtter eksport fra forskellige objekter. Men fordi formålet her er at være i stand til at kopiere eller opdatere en masse poster i en tabel med vedhæftede felter, er den bedste objekttype for os at bruge en gemt forespørgsel. Med en gemt forespørgsel kan vi styre, hvilke rækker der skal indsættes eller opdateres, og vi kan også forme outputtet. Hvis du ser på skemadesignet af MSysResources tabel nedenfor:

Der er et potentielt problem. Når vi bruger temaer eller billeder, henviser vi til varen ved navn, ikke ved id. Men Navn kolonnen er ikke unik og er ikke den primære nøgle i tabellen. Derfor, når vi tilføjer eller opdaterer poster, ønsker vi at matche på Navn kolonne, ikke Id kolonne. Det betyder, at når vi eksporterer, bør vi sandsynligvis ikke inkludere Id kolonne, og vi bør kun eksportere den unikke liste over Navn for at sikre, at ressourcerne ikke pludselig går fra "Open.png" til "Close.png" eller noget fjollet.

Vi opretter derefter en forespørgsel, der skal fungere som kilden til de poster, vi vil importere til MSysResources bord. Lad os starte med denne SQL bare for at demonstrere filtreringen ned til en undergruppe af poster:

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

Vi gemmer den som qryResourcesExport . Vi kan derefter skrive VBA-kode for at eksportere XML:

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

Dette emulerer den eksport, vi oprindeligt udførte interaktivt.

Men hvis vi derefter importerer den resulterende XML, har vi kun mulighed for at tilføje data til en eksisterende tabel. Vi kan ikke kontrollere, hvilken tabel den vil føjes til; den vil finde en tabel eller forespørgselstabel med samme navn (f.eks. qryResourcesExport og tilføje poster til den forespørgsel. Hvis forespørgslen kan opdateres, er der ikke noget problem, og den indsættes i Eksempel som forespørgslen er baseret på. Men hvad hvis den kildeforespørgsel, vi bruger, ikke kan opdateres eller måske ikke eksisterer? I begge tilfælde ville vi ikke være i stand til at importere XML-filen, som den er. Det kan enten mislykkes med at importere eller ende med at skabe en ny tabel med navnet qryResourcesExport som ikke hjælper os. Og hvad med tilfældet med kopiering af data fra Eksempel til MSysResources ? Vi ønsker ikke at tilføje data til Eksemplet tabel.

Det er her TransformXML metoden kommer til undsætning. En fuldstændig diskussion om, hvordan man skriver en XML-transformation, er uden for rækkevidden, men du burde være i stand til at finde rigelige ressourcer til, hvordan man skriver et XSLT-stylesheet til at beskrive transformationen. Der er flere online værktøjer, du kan bruge til at validere din XSLT også. Her er en. For det simple tilfælde, hvor vi blot vil kontrollere, hvilken tabel XML-filen skal tilføje posterne til, kan du komme i gang med denne XSLT-fil. Du kan derefter køre følgende VBA-kode:

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

Vi kan erstatte den originale XML-fil med den transformerede XML-fil, som nu indsættes i MSysResources tabel i stedet for ind i (muligvis ikke-eksisterende forespørgsel/tabel) qryResourcesExport .

Så skal vi håndtere opdateringerne. Fordi vi faktisk tilføjer nye poster og MSysResources tabel ikke har nogen begrænsninger på de dubletnavne, skal vi sikre, at alle eksisterende poster med samme navne først slettes. Dette kan opnås ved at skrive en tilsvarende forespørgsel som sådan:

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

derefter køre det først, før du kører VBA-koden:

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

Fordi XML-filen blev transformeret, er ImportXML metoden vil nu indsætte dataene i MSysResources tabel i stedet for den oprindelige forespørgsel, som vi brugte med ExportXML metode. Vi specificerer, at den skal tilføje data til en eksisterende tabel. Men hvis tabellen ikke eksisterer, vil den blive oprettet.

Og dermed har vi opnået en masseopdatering/indsættelse af tabellen med et vedhæftningsfelt, som er meget hurtigere sammenlignet med den originale recordset-and-child-recordset VBA-kode. Håber det hjælper! Hvis du har brug for hjælp til at udvikle Access-applikationer, er du også velkommen til at kontakte os!


  1. skråstreg før hvert citatproblem

  2. Hvad er den tilsvarende PostgreSQL-syntaks til Oracles CONNECT BY ... START WITH?

  3. Henter alle objektprivilegier for specifik rolle

  4. 900 byte indeksstørrelsesgrænse i tegnlængde