Od Accessu 2010 podporuje Access datový typ Attachments, který se na první pohled jeví jako pohodlná funkce pro ukládání malých obrázků nebo souborů. Rychlé vyhledávání na Googlu však obvykle ukáže, že je lepší se jim vyhnout. To vše se scvrkává na skutečnost, že datový typ Attachments je ve skutečnosti pole s více hodnotami (MVF) a to přináší několik problémů. Za prvé, nebudete moci používat dotazy k vložení nebo aktualizaci několika záznamů najednou. Všechny tabulky, které obsahují takový datový typ, vás skutečně nutí provádět velké množství kódu a už jen z tohoto důvodu se takové datové typy běžně nepoužíváme.
Nicméně je tu problém. Rádi používáme galerii obrázků a motivy, které závisí na systémové tabulce MSysResources
který bohužel používá datové typy příloh. To způsobilo problém se správou zdrojů v naší standardní knihovně, protože chceme používat MSysResources
ale nemůžeme je snadno aktualizovat nebo vložit hromadně.
Datový typ přílohy (stejně jako MVF) vás nutí používat programování „řádek po agonizujícím řádku“ při práci s polem MVF, je to dvojka s polem Přílohy, protože byste museli použít LoadFromFile nebo
Uložit do souboru
metody. Microsoft má článek s příklady o těchto metodách. Při přidávání nových záznamů tedy musíte interagovat se souborovým systémem. Ne vždy ve všech situacích žádoucí. Nyní, pokud kopírujeme z jedné tabulky do jiné tabulky, můžeme se vyhnout skákání přes souborový systém tím, že uděláme něco jako:
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
Holy looping, batmane! To je spousta kódu, vše jen pro kopírování příloh z jedné tabulky do druhé. I když neskáčeme přes souborový systém, je také velmi pomalý. Podle našich zkušeností může tabulka s 1000 záznamy obsahující jednu přílohu trvat minut jen zpracovat. Nyní je to docela předimenzované, když vezmete v úvahu velikost. Stůl s přílohami není tak velký. Ve skutečnosti udělejme experiment. Podívejme se, co se stane, když zkopíruji a vložím přes datový list:
Kopírování a vkládání je tedy prakticky okamžité. Je zřejmé, že kód použitý při vkládání není stejný kód, který bychom použili ve VBA. Jsme však přesvědčeni, že když to dokážeme interaktivně, můžeme to udělat i ve VBA. Můžeme replikovat rychlost interaktivního vkládání ve VBA? Odpověď zní ano, můžeme!
Zrychlete pomocí…. XML?
Překvapivě metoda, která poskytuje nejrychlejší způsob kopírování dat, včetně příloh, je pomocí souborů XML. Přiznám se, že nesahám po souborech XML, s výjimkou řešení omezení. V průměru jsou soubory XML relativně pomalé vůči jiným formátům souborů, ale v tomto případě má XML jednu obrovskou výhodu; nemá problém s popisem MVF. Pojďme vytvořit soubor XML a prozkoumat možnosti, které získáváme při importu/exportu souboru XML.
Po obvyklém dialogovém okně průvodce exportem pro nastavení cesty k uložení souboru XML se nám zobrazí následující dialog:
Pokud poté klikneme na tlačítko „Další možnosti…“, místo toho se zobrazí toto dialogové okno:
Z tohoto dialogu vidíme několik dalších vodítek o tom, co je možné; jmenovitě:
- Máme možnost exportovat celou tabulku nebo pouze podmnožinu tabulky použitím filtru
- Můžeme transformovat výstup XML.
- Schéma můžeme popsat kromě obsahu tabulky.
Zjistil jsem, že nejlepší je vložit schéma; výchozí je exportovat jej, ale jako samostatný soubor. To však může být náchylné k chybám a mohou zapomenout zahrnout soubor XSD do souboru XML. Toto lze změnit na zobrazené kartě schématu:
Dokončeme export a stručně se podíváme na data výsledného souboru XML.
Všimněte si, že přílohy jsou popsány v části Data
podstrom a obsah souboru je kódován base-64. Zkusme importovat soubor XML. Po procházení průvodce importem se nám zobrazí tento dialog:
Všimněte si následujících funkcí:
- Stejně jako u exportu máme možnost transformovat XML.
- Můžeme řídit, zda importovat strukturu, data nebo obojí
Pokud poté dokončíme import souboru XML, zjistíme, že je stejně rychlý jako operace kopírování a vkládání, kterou jsme provedli.
Nyní víme, že existuje lepší cesta ke kopírování několika záznamů s přílohami. Ale v této situaci to chceme dělat programově, spíše než interaktivně. Můžeme udělat to samé, co jsme právě udělali? Odpověď je opět ano. Existuje několik způsobů, jak udělat totéž, ale myslím, že nejjednodušší je použít 3 nové metody, které byly přidány do Aplikace
objekt od Accessu 2010:
ExportXML
metodaTransformXML
metodaImport XML
metoda
Všimněte si, že ExportXML
metoda podporuje export z různých objektů. Protože je zde cílem umět hromadně kopírovat nebo aktualizovat záznamy tabulky s poli příloh, je pro nás nejlepším typem objektu uložený dotaz. Pomocí uloženého dotazu můžeme řídit, které řádky se mají vložit nebo aktualizovat, a také můžeme tvarovat výstup. Pokud se podíváte na návrh schématu MSysResources
tabulka níže:
Existuje potenciální problém. Kdykoli používáme motivy nebo obrázky, odkazujeme na položku jménem, nikoli podle ID. Nicméně Název
sloupec není jedinečný a není primárním klíčem tabulky. Proto, když přidáváme nebo aktualizujeme záznamy, chceme se shodovat s Jméno
sloupec, nikoli ID
sloupec. To znamená, že když exportujeme, pravděpodobně bychom neměli zahrnout Id
a měli bychom exportovat pouze jedinečný seznam Název
aby se zajistilo, že zdroje náhle nepřejdou z „Open.png“ na „Zavřít.png“ nebo něco hloupého.
Poté vytvoříme dotaz, který bude sloužit jako zdroj pro záznamy, které chceme importovat do MSysResources
stůl. Začněme s tímto SQL, abychom demonstrovali filtrování až na podmnožinu záznamů:
SELECT e.Data, e.Extension, e.Name, e.Type FROM Example AS e WHERE e.Name In ("blue","red","green");
Poté jej uložíme jako qryResourcesExport
. Poté můžeme napsat kód VBA pro export XML:
Application.ExportXML _ ObjectType:=acExportQuery, _ DataSource:="qryResourcesExport", _ DataTarget:="C:\Path\to\Resources.xml", _ OtherFlags:=acEmbedSchema
To emuluje export, který jsme původně provedli interaktivně.
Pokud však následně importujeme výsledné XML, máme pouze možnost připojit data k existující tabulce. Nemůžeme kontrolovat, do které tabulky se to připojí; najde tabulku nebo tabulku dotazu se stejným názvem (např. qryResourcesExport
a připojit záznamy do tohoto dotazu. Pokud je dotaz aktualizovatelný, není problém a vloží se do Příklad
na kterém je dotaz založen. Ale co když zdrojový dotaz, který používáme, není aktualizovatelný nebo nemusí existovat? V obou případech bychom nebyli schopni importovat soubor XML tak, jak je. Může se stát, že import selže nebo se vytvoří nová tabulka s názvem qryResourcesExport
což nám nepomůže. A co případ kopírování dat z Příkladu
na MSysResources
? Nechceme přidávat data k Příkladu
tabulka.
Zde se nachází TransformXML
metoda přichází na záchranu. Úplná diskuse o tom, jak napsat transformaci XML, je nad rámec, ale měli byste být schopni najít dostatek zdrojů o tom, jak napsat šablonu stylů XSLT k popisu transformace. Existuje několik online nástrojů, které můžete použít také pro ověření vašeho XSLT. Zde je jeden. Pro jednoduchý případ, kdy chceme pouze řídit, do které tabulky má soubor XML připojovat záznamy, můžete začít s tímto souborem XSLT. Poté můžete spustit následující kód VBA:
Application.TransformXML _ DataSource:="C:\Path\to\Resources.xml", _ TransformSource:="C:\Path\to\ResourcesTransform.xslt", _ OutputTarget:="C:\Path\to\Resources.xml", _ WellFormedXMLOutput:=True, _ ScriptOption:=acEnableScript
Můžeme nahradit původní soubor XML transformovaným souborem XML, který se nyní vloží do MSysResources
tabulky spíše než do (pravděpodobně neexistujícího dotazu/tabulky) qryResourcesExport
.
Pak musíme zpracovat aktualizace. Protože ve skutečnosti připojujeme nové záznamy a MSysResources
tabulka nemá žádná omezení na duplicitní názvy, musíme zajistit, aby byly všechny existující záznamy se stejnými názvy nejprve odstraněny. Toho lze dosáhnout napsáním ekvivalentního dotazu jako takto:
DELETE FROM MSysResources AS r WHERE r.Name In ("blue","red","green");
pak jej nejprve spusťte před spuštěním kódu VBA:
Application.ImportXML DataSource:="C:\Path\to\Resources.xml", ImportOptions:=acAppendData
Protože byl soubor XML transformován, ImportXML
metoda nyní vloží data do MSysResources
namísto původního dotazu, který jsme použili s ExportXML
metoda. Určujeme, že má připojovat data do existující tabulky. Pokud však tabulka neexistuje, bude vytvořena.
A díky tomu jsme dosáhli hromadné aktualizace/vložení tabulky s polem přílohy, která je mnohem rychlejší ve srovnání s původním kódem VBA sady záznamů a podřízených záznamů. Doufám, že to pomůže! Také, pokud potřebujete pomoc s vývojem aplikací Access, neváhejte nás kontaktovat!