sql >> Database >  >> RDS >> Access

Bulksgewijs invoegen of bijwerken voor tabellen met bijlagevelden

Sinds Access 2010 ondersteunt Access het gegevenstype Bijlagen, wat op het eerste gezicht een handige functie lijkt voor het opslaan van kleine afbeeldingen of bestanden. Een snelle google-zoekopdracht zal echter meestal aantonen dat ze het beste kunnen worden vermeden. Dit komt er allemaal op neer dat een gegevenstype Attachments eigenlijk een Multi-Valued Field (MVF) is, en deze brengen verschillende problemen met zich mee. Ten eerste zou u geen query's kunnen gebruiken om meerdere records in één keer in te voegen of bij te werken. Inderdaad, alle tabellen die een dergelijk gegevenstype bevatten, dwingen u om veel code te doen en alleen al om die reden vermijden we het normale gebruik van dergelijke gegevenstypen.

Er is echter een probleem. We gebruiken graag de afbeeldingengalerij en thema's, die beide afhankelijk zijn van een systeemtabel, MSysResources die helaas de gegevenstypen voor bijlagen gebruikt. Dit heeft een probleem veroorzaakt voor het beheren van bronnen in onze standaardbibliotheek omdat we de MSysResources willen gebruiken maar we kunnen ze niet gemakkelijk in bulk bijwerken of invoegen.

Het gegevenstype bijlage (evenals MVF's) dwingt je om "rij-voor-pijnlijke-rij"-programmering te gebruiken bij het omgaan met een MVF-veld, het is een twofer met het veld Bijlagen omdat je de LoadFromFile zou moeten gebruiken code> of SaveToFile methoden. Microsoft heeft een artikel met voorbeelden over die methoden. U moet dus communiceren met het bestandssysteem wanneer u nieuwe records toevoegt. Niet altijd wenselijk in alle situaties. Als we nu van de ene naar de andere tabel kopiëren, kunnen we voorkomen dat we over het bestandssysteem stuiteren door iets te doen als:

Dim SourceParentRs As DAO.Recordset2Dim SourceChildRs As DAO.Recordset2Dim TargetParentRs As DAO.Recordset2Dim TargetChildRs As DAO.Recordset2Dim SourceField As DAO.Field2Set SourceParentRs =db.OpenRecordttachment ", dbOpenDynaset, dbAppendOnly) Doen tot SourceParentRs.EOF TargetParentRs.AddNew For Each SourceField In SourceParentRs.Fields If SourceField.Type <> dbAttachment Then TargetParentRs.Fields(SourceField.Name).ValueR EndField.Name).Value =SourceField. Moet eerst record opslaan voordat MVF-velden kunnen worden bewerkt TargetParentRs.Bookmark =TargetParentRs.LastModified Set SourceChildRs =SourceParentRs.Fields("Data").Value Set TargetChildRs =TargetParentRs.Fields("Data").Value Do Until SourcechildRs.EOF TargetChildRs.Add 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").AppendChildChunk(SourcetChunk(Bron) , ChunkSize) Offset =Offset + ChunkSize Loop TargetChildRs.Update SourceChildRs.MoveNext Loop TargetParentRs.Update SourceParentRs.MoveNextLoop

Heilige looping, Batman! Dat is veel code, allemaal om bijlagen van de ene tabel naar de andere te kopiëren. Ook al stuiteren we niet over het bestandssysteem, het is ook erg traag. In onze ervaring kan een tabel met 1000 records met een enkele bijlage minuten duren gewoon te verwerken. Nu, dit is behoorlijk buitenmaats als je kijkt naar de grootte. De tafel met de opzetstukken is niet zo groot. Laten we in feite een experiment doen. Eens kijken wat er gebeurt als ik kopieer en plak via de datasheet:

Kopiëren en plakken is dus praktisch onmiddellijk. Het is duidelijk dat de code die wordt gebruikt door te plakken niet dezelfde code is die we in VBA zouden gebruiken. We zijn er echter van overtuigd dat als we het interactief kunnen doen, we het ook in VBA kunnen doen. Kunnen we de snelheid van interactief plakken in VBA repliceren? Het antwoord blijkt ja te zijn, dat kunnen we!

Versnel met …. XML?

Verrassend genoeg is de methode die de snelste manier biedt om gegevens te kopiëren, inclusief bijlagen, via XML-bestanden. Ik geef toe dat ik niet naar XML-bestanden reik, behalve als een tijdelijke oplossing voor beperkingen. Gemiddeld zijn XML-bestanden relatief traag in vergelijking met andere bestandsindelingen, maar in dit geval heeft XML één enorm voordeel; het heeft geen probleem om MVF's te beschrijven. Laten we een XML-bestand maken en de mogelijkheden onderzoeken die we krijgen bij het importeren/exporteren van een XML-bestand.

Na het gebruikelijke dialoogvenster van de exportwizard om het pad in te stellen om het XML-bestand op te slaan, krijgen we een dialoogvenster als dit:

Als we vervolgens op de knop "Meer opties..." klikken, krijgen we in plaats daarvan dit dialoogvenster:

In dit dialoogvenster zien we nog enkele aanwijzingen over wat mogelijk is; namelijk:

  • We hebben de mogelijkheid om de hele tabel of slechts een subset van de tabel te exporteren door een filter toe te passen
  • We kunnen de XML-uitvoer transformeren.
  • We kunnen het schema naast de inhoud van de tabel beschrijven.

Ik vind dat het het beste is om het schema in te sluiten; de standaard is om het te exporteren, maar als een apart bestand. Dat kan echter foutgevoelig zijn en ze kunnen vergeten het XSD-bestand bij het XML-bestand te voegen. Dit kan worden gewijzigd via het getoonde schematabblad:

Laten we het exporteren afronden en een snelle blik werpen op de gegevens van het resulterende XML-bestand.

Merk op dat de bijlagen worden beschreven in de Data substructuur en bestandsinhoud is base-64 gecodeerd. Laten we proberen het XML-bestand te importeren. Nadat we de importwizard hebben doorlopen, krijgen we dit dialoogvenster:

Let op de volgende kenmerken:

  • Net als bij exporteren hebben we de mogelijkheid om de XML te transformeren.
  • We kunnen bepalen of we de structuur, de gegevens of beide importeren

Als we dan klaar zijn met het importeren van het XML-bestand, merken we dat het net zo snel gaat als de kopieer- en plakbewerking die we deden.

We weten nu dat er een betere manier is om meerdere records met bijlagen te kopiëren. Maar in deze situatie willen we dit programmatisch doen, in plaats van interactief. Kunnen we hetzelfde doen als wat we net deden? Nogmaals, het antwoord is ja. Er zijn meerdere manieren om hetzelfde te doen, maar ik denk dat de gemakkelijkste methode is om de 3 nieuwe methoden te gebruiken die zijn toegevoegd aan de Applicatie object sinds Access 2010:

  • XML exporteren methode
  • TransformXML methode
  • XML importeren methode

Merk op dat de ExportXML methode ondersteunt het exporteren van verschillende objecten. Omdat het doel hier echter is om de records van een tabel met een bijlageveld massaal te kunnen kopiëren of bijwerken, is het beste objecttype dat we kunnen gebruiken een opgeslagen query. Met een opgeslagen query kunnen we bepalen welke rijen moeten worden ingevoegd of bijgewerkt en kunnen we ook de uitvoer vormgeven. Als je kijkt naar het schema-ontwerp van de MSysResources onderstaande tabel:

Er is een potentieel probleem. Telkens wanneer we thema's of afbeeldingen gebruiken, verwijzen we naar het item met de naam, niet met de ID. Echter, de Naam kolom is niet uniek en is niet de primaire sleutel van de tabel. Daarom willen we, wanneer we records toevoegen of bijwerken, matchen op de Naam kolom, niet de Id kolom. Dit betekent dat wanneer we exporteren, we waarschijnlijk niet de Id . moeten opnemen kolom en we zouden alleen de unieke lijst van de Naam . moeten exporteren om ervoor te zorgen dat de bronnen niet plotseling van "Open.png" naar "Close.png" gaan of iets geks.

We maken dan een query die als bron fungeert voor de records die we willen importeren in de MSysResources tafel. Laten we beginnen met deze SQL om het filteren naar een subset van records te demonstreren:

SELECTEER e.Data, e.Extension, e.Name, e.TypeFROM Voorbeeld AS eWHERE e.Name In ("blauw","rood","groen");

We zullen het dan opslaan als qryResourcesExport . We kunnen dan VBA-code schrijven om XML te exporteren:

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

Dit emuleert de export die we oorspronkelijk interactief deden.

Als we vervolgens de resulterende XML importeren, hebben we alleen de mogelijkheid om gegevens toe te voegen aan een bestaande tabel. We kunnen niet bepalen in welke tabel het wordt toegevoegd; het zal een tabel of querytabel met dezelfde naam vinden (bijv. qryResourcesExport en voeg records toe aan die query. Als de query kan worden bijgewerkt, is er geen probleem en wordt deze ingevoegd in het Voorbeeld waarop de vraag is gebaseerd. Maar wat als de bronquery die we gebruiken niet kan worden bijgewerkt of niet bestaat? In beide gevallen zouden we het XML-bestand niet kunnen importeren zoals het is. Het kan zijn dat het niet kan worden geïmporteerd of dat er een nieuwe tabel wordt gemaakt met de naam qryResourcesExport wat ons niet helpt. En hoe zit het met het kopiëren van gegevens uit Voorbeeld naar MSysResources ? We willen geen gegevens toevoegen aan het Voorbeeld tafel.

Dat is waar de TransformXML methode komt te hulp. Een volledige discussie over het schrijven van een XML-transformatie valt buiten het bestek, maar u zou voldoende bronnen moeten kunnen vinden over het schrijven van een XSLT-stylesheet om de transformatie te beschrijven. Er zijn ook verschillende online tools die u kunt gebruiken om uw XSLT te valideren. Hier is er een. Voor het eenvoudige geval waarin we alleen willen bepalen aan welke tabel het XML-bestand de records moet toevoegen, kunt u aan de slag gaan met dit XSLT-bestand. U kunt dan de volgende VBA-code uitvoeren:

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

We kunnen het originele XML-bestand vervangen door het getransformeerde XML-bestand, dat nu wordt ingevoegd in de MSysResources tabel in plaats van in (mogelijk niet-bestaande query/tabel) qryResourcesExport .

We moeten dan de updates afhandelen. Omdat we eigenlijk nieuwe records toevoegen, en de MSysResources tabel geen beperkingen heeft voor de dubbele namen, moeten we ervoor zorgen dat alle bestaande records met dezelfde namen eerst worden verwijderd. Dit kan worden bereikt door een gelijkwaardige zoekopdracht te schrijven, zoals:

VERWIJDEREN VAN MSysResources AS rWHERE r.Name In ("blauw", "rood", "groen");

voer het vervolgens eerst uit voordat u de VBA-code uitvoert:

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

Omdat het XML-bestand is getransformeerd, is de ImportXML methode zal nu de gegevens invoegen in de MSysResources tabel in plaats van de oorspronkelijke query die we gebruikten met de ExportXML methode. We specificeren dat het gegevens moet toevoegen aan een bestaande tabel. Als de tabel echter niet bestaat, wordt deze gemaakt.

En daarmee hebben we een massale update/invoeging van de tabel met een bijlageveld bereikt die veel sneller is in vergelijking met de originele recordset-and-child-recordset VBA-code. Hoop dat dat helpt! Als je hulp nodig hebt bij het ontwikkelen van Access-applicaties, neem dan gerust contact met ons op!


  1. Inzicht in 'datetime2' opslaggrootte in SQL Server

  2. geheugenefficiënte ingebouwde SqlAlchemy iterator/generator?

  3. 6 manieren om dubbele rijen in Oracle te selecteren

  4. Hoe InfluxDB te installeren op Ubuntu 20.10