От Access 2010 Access поддържа тип данни прикачени файлове, който на повърхността изглежда като удобна функция за съхранение на малки изображения или файлове. Въпреки това, бързо търсене в Google обикновено показва, че е най-добре да се избягват. Всичко това се свежда до факта, че типът данни за прикачени файлове всъщност е многозначно поле (MVF) и те идват с няколко проблема. От една страна, няма да можете да използвате заявки за вмъкване или актуализиране на няколко записа наведнъж. Всъщност всички таблици, които съдържат такъв тип данни, ви принуждават да правите много код и само поради тази причина избягваме да използваме такива типове данни нормално.
Има обаче проблем. Обичаме да използваме галерията с изображения и темите, като и двете зависят от системна таблица, MSysResources
който за съжаление използва типовете данни за прикачени файлове. Това създаде проблем за управлението на ресурси в нашата стандартна библиотека, защото искаме да използваме MSysResources
но не можем лесно да ги актуализираме или вмъкваме групово.
Типът данни за прикачени файлове (както и MVFs) ви принуждава да използвате програмиране „ред по-агонизиращ ред“, когато работите с поле MVF, това е полето с прикачени файлове, защото ще трябва да използвате LoadFromFileкод> или
SaveToFile
методи. Microsoft има статия с примери за тези методи. По този начин трябва да взаимодействате с файловата система, когато добавяте нови записи. Не винаги е желателно във всички ситуации. Сега, ако копираме от една таблица в друга таблица, можем да избегнем прескачането на файловата система, като направим нещо като:
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
Свети лупинг, батман! Това е много код, всичко само за копиране на прикачени файлове от една таблица в друга. Въпреки че не прескачаме файловата система, тя също е много бавна. Според нашия опит таблица с 1000 записа, съдържащи един прикачен файл, може да отнеме минути само за обработка. Сега това е доста огромен, като се има предвид размерът. Масата с приставките не е толкова голяма. Всъщност, нека направим експеримент. Нека видим какво ще се случи, ако копирам и поставям чрез листа с данни:
Така че копирането и поставянето става практически мигновено. Очевидно кодът, използван при поставяне, не е същият код, който бихме използвали във VBA. Въпреки това, ние силно вярваме, че ако можем да го направим интерактивно, можем да го направим и във VBA. Можем ли да повторим скоростта на интерактивното поставяне във VBA? Отговорът се оказва да, можем!
Ускорете с.... XML?
Изненадващо методът, който осигурява най-бързия начин за копиране на данни, включително прикачени файлове, е чрез XML файлове. Ще призная, че не посягам към XML файлове, освен като заобиколно решение за ограничения. Средно XML файловете са относително бавни спрямо други файлови формати, но в този случай XML има едно огромно предимство; няма проблем с описанието на MVF. Нека създадем XML файл и да проучим възможностите, които получаваме с импортирането/експортирането на XML файл.
След обичайния диалогов прозорец на съветника за експортиране, за да зададете пътя за запазване на XML файла, ще получим диалогов прозорец като този:
Ако след това щракнем върху бутона „Още опции...“, вместо това получаваме този диалогов прозорец:
От този диалог виждаме още няколко улики за това какво е възможно; а именно:
- Имаме опцията да експортираме цялата таблица или само подмножество от таблицата чрез прилагане на филтър
- Можем да трансформираме XML изхода.
- Можем да опишем схемата в допълнение към съдържанието на таблицата.
Намирам, че е най-добре да вградите схемата; по подразбиране е да се експортира, но като отделен файл. Това обаче може да бъде податливо на грешки и те могат да забравят да включат XSD файла с XML файла. Това може да се промени чрез показания раздел на схемата:
Нека приключим с експортирането му и да разгледаме бързо данните на получения XML файл.
Имайте предвид, че прикачените файлове са описани в Данни
поддървото и съдържанието на файла е кодирано с база 64. Нека опитаме да импортираме XML файла. След като преминем през съветника за импортиране, ще получим този диалогов прозорец:
Обърнете внимание на следните функции:
- Както при експортирането, ние имаме опцията да трансформираме XML.
- Можем да контролираме дали да импортираме структурата, данните или и двете
Ако след това приключим с импортирането на XML файла, ще открием, че той е също толкова бърз, колкото и операцията copy’paste, която направихме.
Вече знаем, че има по-добър път за копиране на няколко записа с прикачени файлове. Но в тази ситуация искаме да направим това програмно, а не интерактивно. Можем ли да направим същото, което направихме току-що? Отново отговорът е да. Има няколко начина да направите едно и също нещо, но мисля, че най-лесният метод е да използвате 3-те нови метода, които бяха добавени към Application
обект от Access 2010:
ExportXML
методTransformXML
методImportXML
метод
Имайте предвид, че ExportXML
методът поддържа експортиране от различни обекти. Въпреки това, тъй като целта тук е да можем да копираме или актуализираме масово записите на таблица с полета за прикачени файлове, най-добрият тип обект, който да използваме, е запазена заявка. Със запазена заявка можем да контролираме кои редове да бъдат вмъкнати или актуализирани и също така можем да оформим изхода. Ако погледнете дизайна на схемата на MSysResources
таблица по-долу:
Има потенциален проблем. Всеки път, когато използваме теми или изображения, препращаме към елемента по име, а не по идентификатор. Въпреки това, Име
колоната не е уникална и не е първичен ключ на таблицата. Следователно, когато добавяме или актуализираме записи, искаме да съвпадаме в Име
колона, а не Id
колона. Това означава, че когато експортираме, вероятно не трябва да включваме Id
колона и трябва да експортираме само уникалния списък на Име
за да гарантирате, че ресурсите няма да преминат внезапно от „Open.png“ към „Close.png“ или нещо глупаво.
След това ще създадем заявка, която да действа като източник на записите, които искаме да импортираме в MSysResources
маса. Нека започнем с този SQL, само за да демонстрираме филтрирането до подмножество от записи:
SELECT e.Data, e.Extension, e.Name, e.Type FROM Example AS e WHERE e.Name In ("blue","red","green");
След това ще го запишем като qryResourcesExport
. След това можем да напишем VBA код за експортиране на XML:
Application.ExportXML _ ObjectType:=acExportQuery, _ DataSource:="qryResourcesExport", _ DataTarget:="C:\Path\to\Resources.xml", _ OtherFlags:=acEmbedSchema
Това емулира експорта, който първоначално направихме интерактивно.
Ако обаче импортираме получения XML, имаме възможност само да добавим данни към съществуваща таблица. Не можем да контролираме в коя таблица ще се добави; той ще намери таблица или таблица със заявка със същото име (напр. qryResourcesExport
и добавете записи към тази заявка. Ако заявката може да се актуализира, тогава няма проблем и тя ще се вмъкне в Пример
на която се основава заявката. Но какво ще стане, ако изходната заявка, която използваме, не може да се актуализира или може да не съществува? И в двата случая няма да можем да импортираме XML файла такъв, какъвто е. Може или да не успее да импортира, или в крайна сметка да създаде нова таблица с име qryResourcesExport
което не ни помага. А какво да кажем за случая на копиране на данни от Пример
към MSysResources
? Не искаме да добавяме данни към Пример
таблица.
Това е мястото, където TransformXML
методът идва на помощ. Пълна дискусия за това как да напишете XML трансформация е извън обхвата, но трябва да можете да намерите достатъчно ресурси за това как да напишете XSLT стилова таблица, за да опишете трансформацията. Има няколко онлайн инструмента, които можете да използвате и за валидиране на вашия XSLT. Ето едно. За простия случай, в който просто искаме да контролираме в коя таблица XML файлът трябва да добави записите, можете да започнете с този XSLT файл. След това можете да стартирате следния 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
Можем да заменим оригиналния XML файл с трансформирания XML файл, който сега ще се вмъкне в MSysResources
таблица, а не в (евентуално несъществуваща заявка/таблица) qryResourcesExport
.
След това трябва да се справим с актуализациите. Защото всъщност добавяме нови записи и MSysResources
таблицата няма ограничения за дублиращите се имена, трябва да гарантираме, че всички съществуващи записи със същите имена първо ще бъдат изтрити. Това може да се постигне чрез написване на еквивалентна заявка, както следва:
DELETE FROM MSysResources AS r WHERE r.Name In ("blue","red","green");
след това първо го стартирайте, преди да стартирате VBA кода:
Application.ImportXML DataSource:="C:\Path\to\Resources.xml", ImportOptions:=acAppendData
Тъй като XML файлът е трансформиран, ImportXML
сега методът ще вмъкне данните в MSysResources
таблица, а не оригиналната заявка, която използвахме с ExportXML
метод. Уточняваме, че трябва да добавя данни в съществуваща таблица. Ако обаче таблицата не съществува, тя ще бъде създадена.
И с това постигнахме масова актуализация/вмъкване на таблицата с поле за прикачени файлове, което е много по-бързо в сравнение с оригиналния VBA код за набор от записи и дете. Надявам се това да помогне! Освен това, ако имате нужда от помощ при разработването на приложения на Access, не се колебайте да се свържете с нас!