Od 2010 r. program Access obsługuje typ danych Załączniki, który na pierwszy rzut oka wydaje się wygodną funkcją do przechowywania małych obrazów lub plików. Jednak szybkie wyszukiwanie w Google zwykle pokazuje, że najlepiej ich unikać. Wszystko sprowadza się do tego, że typ danych Załączniki jest w rzeczywistości polem wielowartościowym (MVF), a te wiążą się z kilkoma problemami. Po pierwsze, nie możesz używać zapytań do wstawiania lub aktualizowania kilku rekordów za jednym razem. Rzeczywiście, wszelkie tabele zawierające taki typ danych zmuszają do wykonania dużej ilości kodu i tylko z tego powodu unikamy normalnego używania takich typów danych.
Jest jednak problem. Uwielbiamy używać galerii obrazów i motywów, które zależą od tabeli systemowej MSysResources
który niestety wykorzystuje typy danych załączników. Stworzyło to problem z zarządzaniem zasobami w naszej standardowej bibliotece, ponieważ chcemy użyć MSysResources
ale nie możemy łatwo zaktualizować lub wstawić ich zbiorczo.
Typ danych załącznika (jak również MVF) zmusza do korzystania z programowania „wiersz po wierszu” podczas pracy z polem MVF, jest to dwójka z polem Załączniki, ponieważ musiałbyś użyć LoadFromFile lub
SaveToFile
metody. Microsoft ma artykuł z przykładami na temat tych metod. Dlatego musisz wchodzić w interakcję z systemem plików podczas dodawania nowych rekordów. Nie zawsze pożądane we wszystkich sytuacjach. Teraz, jeśli kopiujemy z jednej tabeli do drugiej, możemy uniknąć odbijania się od systemu plików, wykonując coś takiego:
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
Święta pętla, batman! To dużo kodu, a wszystko po to, aby skopiować załączniki z jednej tabeli do drugiej. Mimo że nie odbijamy się od systemu plików, jest on również bardzo powolny. Z naszego doświadczenia wynika, że tabela z 1000 rekordów zawierających jeden załącznik może zająć minuty tylko do przetworzenia. Teraz jest to dość przesadne, biorąc pod uwagę rozmiar. Stół z przystawkami nie jest taki duży. Właściwie zróbmy eksperyment. Zobaczmy, co się stanie, jeśli skopiuję i wkleję za pomocą arkusza danych:

Tak więc kopiowanie i wklejanie jest praktycznie natychmiastowe. Oczywiście kod używany przez wklejanie nie jest tym samym kodem, którego używalibyśmy w VBA. Wierzymy jednak, że jeśli możemy to zrobić interaktywnie, możemy to zrobić również w VBA. Czy możemy powtórzyć szybkość interaktywnego wklejania w VBA? Okazuje się, że odpowiedź brzmi:tak, możemy!
Przyspiesz dzięki…. XML?
Co zaskakujące, najszybszą metodą kopiowania danych, w tym załączników, jest wykorzystanie plików XML. Przyznam, że nie sięgam po pliki XML, chyba że jako obejście ograniczeń. Średnio pliki XML są stosunkowo wolne w stosunku do innych formatów plików, ale w tym przypadku XML ma jedną ogromną zaletę; nie ma problemu z opisaniem MVF. Stwórzmy plik XML i zbadajmy możliwości, jakie otrzymujemy dzięki importowaniu/eksportowaniu pliku XML.

Po zwykłym oknie dialogowym kreatora eksportu, aby ustawić ścieżkę do zapisania pliku XML, pojawi się takie okno dialogowe:

Jeśli następnie klikniemy przycisk „Więcej opcji…”, zamiast tego pojawi się następujące okno dialogowe:

Z tego okna dialogowego widzimy jeszcze kilka wskazówek dotyczących tego, co jest możliwe; mianowicie:
- Mamy możliwość wyeksportowania całej tabeli lub tylko jej podzbioru przez zastosowanie filtra
- Możemy przekształcić wyjście XML.
- Możemy opisać schemat oprócz zawartości tabeli.
Uważam, że najlepiej jest osadzić schemat; domyślnie jest eksportowane, ale jako osobny plik. Jednak może to być podatne na błędy i mogą zapomnieć o dołączeniu pliku XSD do pliku XML. Można to zmienić za pomocą pokazanej zakładki schematu:

Zakończmy eksportowanie i rzućmy okiem na dane wynikowego pliku XML.

Zwróć uwagę, że załączniki są opisane w Dane
zawartość poddrzewa i pliku jest zakodowana w base-64. Spróbujmy zaimportować plik XML. Po przejściu przez kreatora importu pojawi się następujące okno dialogowe:

Zwróć uwagę na następujące funkcje:
- Podobnie jak w przypadku eksportu, mamy możliwość przekształcenia XML.
- Możemy kontrolować, czy importować strukturę, dane, czy jedno i drugie
Jeśli następnie zakończymy importowanie pliku XML, okaże się, że jest to tak samo szybkie, jak wykonana przez nas operacja kopiowania i wklejania.
Teraz wiemy, że istnieje lepsza ścieżka do kopiowania kilku rekordów z załącznikami. Ale w tej sytuacji chcemy to zrobić programowo, a nie interaktywnie. Czy możemy zrobić to samo, co właśnie zrobiliśmy? Ponownie, odpowiedź brzmi:tak. Istnieje wiele sposobów na zrobienie tego samego, ale myślę, że najłatwiejszą metodą jest użycie 3 nowych metod, które zostały dodane do Aplikacji
obiekt od Access 2010:
Eksport XML
metoda
TransformXML
metoda
Import XML
metoda
Zauważ, że ExportXML
metoda obsługuje eksportowanie z różnych obiektów. Ponieważ jednak celem jest tutaj możliwość masowego kopiowania lub aktualizowania rekordów tabeli z polami załączników, najlepszym typem obiektu, którego możemy użyć, jest zapisane zapytanie. Dzięki zapisanemu zapytaniu możemy kontrolować, które wiersze mają zostać wstawione lub zaktualizowane, a także możemy kształtować wynik. Jeśli spojrzysz na projekt schematu MSysResources
tabela poniżej:

Istnieje potencjalny problem. Za każdym razem, gdy używamy motywów lub obrazów, odwołujemy się do przedmiotu według nazwy, a nie identyfikatora. Jednak Nazwa
kolumna nie jest unikatowa i nie jest kluczem podstawowym tabeli. Dlatego kiedy dodajemy lub aktualizujemy rekordy, chcemy dopasować na Nazwie
kolumna, a nie Id
kolumna. Oznacza to, że podczas eksportu prawdopodobnie nie powinniśmy dołączać Id
kolumnę i powinniśmy wyeksportować tylko unikalną listę Nazwy
aby upewnić się, że zasoby nie zmienią się nagle z „Open.png” na „Close.png” lub coś głupiego.
Następnie utworzymy zapytanie, które będzie działać jako źródło rekordów, które chcemy zaimportować do MSysResources
stół. Zacznijmy od tego kodu SQL, aby zademonstrować filtrowanie do podzbioru rekordów:
SELECT e.Data, e.Extension, e.Name, e.Type
FROM Example AS e
WHERE e.Name In ("blue","red","green");
Następnie zapiszemy go jako qryResourcesExport
. Następnie możemy napisać kod VBA, aby wyeksportować XML:
Application.ExportXML _
ObjectType:=acExportQuery, _
DataSource:="qryResourcesExport", _
DataTarget:="C:\Path\to\Resources.xml", _
OtherFlags:=acEmbedSchema
To emuluje eksport, który pierwotnie wykonaliśmy interaktywnie.
Jeśli jednak następnie zaimportujemy wynikowy XML, mamy tylko możliwość dołączenia danych do istniejącej tabeli. Nie możemy kontrolować, do której tabeli zostanie dodany; znajdzie tabelę lub tabelę zapytań o tej samej nazwie (np. qryResourcesExport
i dołącz rekordy do tego zapytania. Jeśli zapytanie można aktualizować, nie ma problemu i zostanie wstawione do Przykładu
na którym opiera się zapytanie. Ale co, jeśli używane przez nas zapytanie źródłowe nie może być aktualizowane lub może nie istnieć? W obu przypadkach nie moglibyśmy zaimportować pliku XML bez zmian. Import może się nie powieść lub zakończyć tworzenie nowej tabeli o nazwie qryResourcesExport
co nam nie pomaga. A co z przypadkiem kopiowania danych z Przykładu
do MSysResources
? Nie chcemy dołączać danych do Przykładu
tabela.
To właśnie tam TransformXML
metoda przychodzi na ratunek. Pełna dyskusja na temat pisania transformacji XML wykracza poza zakres, ale powinieneś być w stanie znaleźć obszerne zasoby dotyczące pisania arkusza stylów XSLT opisującego transformację. Istnieje również kilka narzędzi online, których możesz użyć do walidacji swojego XSLT. Tu jest jeden. W prostym przypadku, w którym chcemy tylko kontrolować, do której tabeli plik XML powinien dołączać rekordy, możesz zacząć od tego pliku XSLT. Następnie możesz uruchomić następujący kod 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
Możemy zastąpić oryginalny plik XML przekształconym plikiem XML, który zostanie teraz wstawiony do MSysResources
tabeli zamiast do (prawdopodobnie nieistniejącego zapytania/tabeli) qryResourcesExport
.
Następnie musimy zająć się aktualizacjami. Ponieważ faktycznie dołączamy nowe rekordy, a MSysResources
tabela nie ma żadnych ograniczeń co do zduplikowanych nazw, musimy upewnić się, że wszelkie istniejące rekordy o tych samych nazwach zostaną najpierw usunięte. Można to osiągnąć, pisząc równoważne zapytanie, takie jak:
DELETE FROM MSysResources AS r
WHERE r.Name In ("blue","red","green");
następnie uruchom go najpierw przed uruchomieniem kodu VBA:
Application.ImportXML DataSource:="C:\Path\to\Resources.xml", ImportOptions:=acAppendData
Ponieważ plik XML został przekształcony, ImportXML
metoda wstawi teraz dane do MSysResources
zamiast oryginalnego zapytania, którego użyliśmy z ExportXML
metoda. Określamy, że powinien dołączyć dane do istniejącej tabeli. Jeśli jednak tabela nie istnieje, zostanie utworzona.
Dzięki temu uzyskaliśmy masową aktualizację/wstawienie tabeli z polem załącznika, które jest znacznie szybsze w porównaniu do oryginalnego kodu VBA zestawu rekordów i zestawu rekordów podrzędnych. Mam nadzieję, że to pomoże! Ponadto, jeśli potrzebujesz pomocy przy tworzeniu aplikacji Access, skontaktuj się z nami!