sql >> Databasteknik >  >> RDS >> Access

Massinlägg eller uppdatering för tabeller med bifogade fält

Sedan Access 2010 har Access stödt bilagor datatyp som på ytan verkar vara en bekväm funktion för att lagra små bilder eller filer. Men en snabb sökning på google visar vanligtvis att de bäst undviks. Allt detta kokar ner till det faktum att en datatyp för bilagor faktiskt är ett multi-Valued Field (MVF), och dessa kommer med flera problem. För det första skulle du inte kunna använda frågor för att infoga eller uppdatera flera poster på en gång. Faktum är att alla tabeller som innehåller sådana datatyper tvingar dig att göra mycket kod och bara av den anledningen undviker vi att använda sådana datatyper normalt.

Det finns dock ett problem. Vi älskar att använda bildgalleriet och teman, som båda beror på en systemtabell, MSysResources som tyvärr använder bifogade datatyper. Detta har skapat ett problem för att hantera resurser i vårt standardbibliotek eftersom vi vill använda MSysResources men vi kan inte enkelt uppdatera eller infoga dem samtidigt.

Bifogade datatyp (liksom MVFs) tvingar dig att använda "rad-för-plågande-rad"-programmering när du hanterar ett MVF-fält, det är en tvåfas med bilagor eftersom du skulle behöva använda LoadFromFile eller SaveToFile metoder. Microsoft har en artikel med exempel om dessa metoder. Därför måste du interagera med filsystemet när du lägger till nya poster. Inte alltid önskvärt i alla situationer. Om vi ​​nu kopierar från en tabell till en annan tabell kan vi undvika att studsa över filsystemet genom att göra något 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

Heliga looping, batman! Det är mycket kod, allt bara för att kopiera bilagor från en tabell till en annan. Även om vi inte studsar över filsystemet är det också väldigt långsamt. Enligt vår erfarenhet kan en tabell med 1000 poster som innehåller en enda bilaga ta minuter bara att bearbeta. Nu är det här ganska överdimensionerat när man tänker på storleken. Bordet med fästena är inte så stort. Faktum är att låt oss göra ett experiment. Låt oss se vad som händer om jag kopierar och klistrar in via datablad:

Så att kopiera och klistra är praktiskt taget omedelbart. Uppenbarligen är koden som används för att klistra in inte samma kod som vi skulle använda i VBA. Men vi är övertygade om att om vi kan göra det interaktivt så kan vi göra det i VBA också. Kan vi replikera hastigheten för interaktiv inklistring i VBA? Svaret visar sig vara ja, det kan vi!

Snabb fart med …. XML?

Överraskande nog är den metod som ger det snabbaste sättet att kopiera data, inklusive bilagor, via XML-filer. Jag kommer att erkänna att jag inte når XML-filer förutom som en lösning för begränsningar. I genomsnitt är XML-filer relativt långsamma till andra filformat, men i det här fallet har XML en stor fördel; det har inga problem att beskriva MVF:er. Låt oss skapa en XML-fil och undersöka vilka möjligheter vi får med att importera/exportera en XML-fil.

Efter den vanliga exportguidens dialogruta för att ställa in sökvägen för att spara XML-filen, kommer vi att få en dialogruta så här:

Om vi ​​sedan klickar på knappen "Fler alternativ..." får vi den här dialogrutan istället:

Från den här dialogrutan ser vi några fler ledtrådar om vad som är möjligt; nämligen:

  • Vi har möjlighet att exportera hela tabellen eller bara en delmängd av tabellen genom att använda ett filter
  • Vi kan transformera XML-utdata.
  • Vi kan beskriva schemat utöver innehållet i tabellen.

Jag tycker att det är bäst att bädda in schemat; standarden är att exportera den men som en separat fil. Det kan dock vara felbenäget och de kan glömma att inkludera XSD-filen med XML-filen. Detta kan ändras via schemafliken som visas:

Låt oss avsluta exporten och ta en snabb titt på den resulterande XML-filens data.

Observera att bilagorna beskrivs i Data underträd och filinnehåll är base-64-kodat. Låt oss försöka importera XML-filen. Efter att ha gått igenom importguiden får vi den här dialogrutan:

Notera följande funktioner:

  • Som med export har vi möjlighet att transformera XML.
  • Vi kan kontrollera om strukturen, data eller båda ska importeras

Om vi ​​sedan avslutar importen av XML-filen, upptäcker vi att den är lika snabb som kopieringen och klistra in.

Vi vet nu att det finns en bättre väg att kopiera flera poster med bilagor. Men i den här situationen vill vi göra detta programmatiskt, snarare än interaktivt. Kan vi göra samma sak som vi just gjorde? Återigen är svaret ja. Det finns flera sätt att göra samma sak men jag tror att den enklaste metoden är att använda de 3 nya metoderna som lades till i Applikationen objekt sedan Access 2010:

  • ExporteraXML metod
  • TransformXML metod
  • ImportXML metod

Observera att ExportXML metod stöder export från olika objekt. Men eftersom målet här är att kunna kopiera eller uppdatera en masse poster i en tabell med bifogade fält, är den bästa objekttypen för oss att använda en sparad fråga. Med en sparad fråga kan vi styra vilka rader som ska infogas eller uppdateras och vi kan även forma utdata. Om du tittar på schemadesignen för MSysResources tabellen nedan:

Det finns ett potentiellt problem. När vi använder teman eller bilder hänvisar vi till objektet med namn, inte med ID. Men Namn kolumnen är inte unik och är inte den primära nyckeln i tabellen. När vi lägger till eller uppdaterar poster vill vi därför matcha Name kolumnen, inte Id kolumn. Det betyder att när vi exporterar bör vi förmodligen inte inkludera Id kolumnen och vi bör endast exportera den unika listan med Namn för att säkerställa att resurserna inte plötsligt går från "Open.png" till "Close.png" eller något dumt.

Vi skapar sedan en fråga för att fungera som källa för de poster vi vill importera till MSysResources tabell. Låt oss börja med denna SQL bara för att demonstrera nedfiltreringen till en delmängd av poster:

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

Vi kommer sedan att spara det som qryResourcesExport . Vi kan sedan skriva VBA-kod för att exportera XML:

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

Detta emulerar exporten som vi ursprungligen gjorde interaktivt.

Men om vi sedan importerar den resulterande XML-en har vi bara möjlighet att lägga till data till en befintlig tabell. Vi kan inte kontrollera vilken tabell den ska läggas till i; den kommer att hitta en tabell eller frågetabell med samma namn (t.ex. qryResourcesExport och lägg till poster i den frågan. Om frågan är uppdateringsbar är det inga problem och den infogas i Exempel som frågan baseras på. Men vad händer om källfrågan vi använder inte är uppdateringsbar eller kanske inte existerar? I båda fallen skulle vi inte kunna importera XML-filen som den är. Det kan antingen misslyckas att importera eller skapa en ny tabell med namnet qryResourcesExport som inte hjälper oss. Och hur är det med fallet med kopiering av data från Exempel till MSysResources ? Vi vill inte lägga till data till Exemplet bord.

Det är där TransformXML metoden kommer till undsättning. En fullständig diskussion om hur man skriver en XML-transformation ligger utanför räckvidden men du bör kunna hitta gott om resurser om hur man skriver en XSLT-formatmall för att beskriva transformationen. Det finns flera onlineverktyg du kan använda för att validera din XSLT också. Här är en. För det enkla fallet där vi bara vill styra vilken tabell XML-filen ska lägga till posterna i, kan du komma igång med denna XSLT-fil. Du kan sedan köra följande VBA-kod:

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 ersätta den ursprungliga XML-filen med den transformerade XML-filen, som nu kommer att infogas i MSysResources tabell snarare än in i (möjligen obefintlig fråga/tabell) qryResourcesExport .

Vi måste sedan hantera uppdateringarna. Eftersom vi faktiskt lägger till nya poster och MSysResources Tabellen inte har några begränsningar för dubblettnamnen, vi måste se till att alla befintliga poster med samma namn först tas bort. Detta kan åstadkommas genom att skriva en motsvarande fråga som så:

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

kör sedan den först innan du kör VBA-koden:

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

Eftersom XML-filen transformerades, har ImportXML metod kommer nu att infoga data i MSysResources tabell istället för den ursprungliga frågan som vi använde med ExportXML metod. Vi anger att den ska lägga till data i en befintlig tabell. Men om tabellen inte finns skapas den.

Och med det har vi uppnått en massuppdatering/insättning av tabellen med ett bifogat fält som är mycket snabbare i jämförelse med den ursprungliga VBA-koden för recordset- och child-recordset. Hoppas det hjälper! Om du behöver hjälp med att utveckla Access-applikationer, kontakta oss också!


  1. Skapa en relation i SQL

  2. Få skillnad i år mellan två datum i MySQL som ett heltal

  3. SQL Server - inre koppling vid uppdatering

  4. SSRS-rapportdefinitionen är nyare än Server