Access
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Access

Chèn hàng loạt hoặc Cập nhật cho các bảng có trường Phần đính kèm

Kể từ Access 2010, Access đã hỗ trợ kiểu dữ liệu Phần đính kèm, bề ngoài có vẻ như là một tính năng thuận tiện để lưu trữ hình ảnh hoặc tệp nhỏ. Tuy nhiên, một tìm kiếm nhanh trên google thường sẽ cho thấy rằng chúng tốt nhất nên tránh. Tất cả những điều này chỉ dẫn đến thực tế là kiểu dữ liệu Phần đính kèm thực sự là một Trường nhiều giá trị (MVF) và những điều này đi kèm với một số vấn đề. Đối với một, bạn không thể sử dụng các truy vấn để chèn hoặc cập nhật nhiều bản ghi cùng một lúc. Thật vậy, bất kỳ bảng nào có chứa kiểu dữ liệu như vậy buộc bạn phải thực hiện rất nhiều mã và chỉ vì lý do đó, chúng tôi tránh sử dụng các kiểu dữ liệu như vậy một cách bình thường.

Tuy nhiên, có một vấn đề. Chúng tôi thích sử dụng thư viện hình ảnh và chủ đề, cả hai đều phụ thuộc vào bảng hệ thống, MSysResources mà không may sử dụng các kiểu dữ liệu đính kèm. Điều này đã tạo ra sự cố khi quản lý tài nguyên trong thư viện chuẩn của chúng tôi vì chúng tôi muốn sử dụng MSysResources nhưng chúng tôi không thể dễ dàng cập nhật hoặc chèn chúng hàng loạt.

Kiểu dữ liệu phần đính kèm (cũng như các MVF) buộc bạn phải sử dụng lập trình “hàng theo hàng” khi xử lý trường MVF, trường này phù hợp với trường Phần đính kèm vì bạn sẽ phải sử dụng LoadFromFile hoặc SaveToFile các phương pháp. Microsoft có một bài báo với các ví dụ về các phương pháp đó. Do đó, bạn phải tương tác với hệ thống tệp khi thêm bản ghi mới. Không phải lúc nào cũng mong muốn trong mọi tình huống. Bây giờ, nếu chúng tôi đang sao chép từ bảng này sang bảng khác, chúng tôi có thể tránh bị trả lại hệ thống tệp bằng cách thực hiện một số việc như:

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

Lặp lại thần thánh, người dơi! Đó là rất nhiều mã, tất cả chỉ để sao chép tệp đính kèm từ bảng này sang bảng khác. Mặc dù chúng tôi không thoát qua hệ thống tệp, nhưng nó cũng rất chậm. Theo kinh nghiệm của chúng tôi, một bảng có 1000 bản ghi chứa một tệp đính kèm có thể mất phút chỉ để xử lý. Bây giờ, điều này là khá lớn khi bạn xem xét kích thước. Bảng với các tập tin đính kèm không phải là lớn. Trên thực tế, chúng ta hãy làm một thử nghiệm. Hãy xem điều gì sẽ xảy ra nếu tôi sao chép và dán qua biểu dữ liệu:

Vì vậy, việc sao chép và dán thực tế là tức thời. Rõ ràng là mã được sử dụng để dán không giống với mã mà chúng ta sẽ sử dụng trong VBA. Tuy nhiên, chúng tôi rất tin tưởng rằng nếu chúng tôi có thể làm điều đó một cách tương tác, chúng tôi cũng có thể làm điều đó trong VBA. Chúng ta có thể nhân rộng tốc độ dán tương tác trong VBA không? Câu trả lời hóa ra là có, chúng ta có thể!

Tăng tốc với…. XML?

Đáng ngạc nhiên là phương pháp cung cấp cách nhanh nhất để sao chép dữ liệu, bao gồm cả tệp đính kèm là thông qua tệp XML. Tôi sẽ thừa nhận rằng tôi không tiếp cận với các tệp XML ngoại trừ một giải pháp thay thế cho các hạn chế. Trung bình, các tệp XML tương đối chậm so với các định dạng tệp khác nhưng trong trường hợp này, XML có một lợi thế rất lớn; nó không có vấn đề gì khi mô tả các MVF. Hãy tạo một tệp XML và điều tra các khả năng chúng ta có được khi nhập / xuất tệp XML.

Sau hộp thoại trình hướng dẫn xuất thông thường để đặt đường dẫn lưu tệp XML, chúng ta sẽ nhận được hộp thoại như sau:

Sau đó, nếu chúng tôi nhấp vào nút “Thêm tùy chọn…”, chúng tôi sẽ nhận được hộp thoại này thay thế:

Từ cuộc đối thoại này, chúng ta thấy thêm một số manh mối về những gì có thể xảy ra; cụ thể là:

  • Chúng tôi có tùy chọn xuất toàn bộ bảng hoặc chỉ một tập hợp con của bảng bằng cách áp dụng bộ lọc
  • Chúng tôi có thể chuyển đổi đầu ra XML.
  • Chúng tôi có thể mô tả giản đồ ngoài nội dung của bảng.

Tôi thấy rằng tốt nhất là nhúng lược đồ; mặc định là xuất nó nhưng dưới dạng một tệp riêng biệt. Tuy nhiên, điều đó có thể dễ xảy ra lỗi và họ có thể quên bao gồm tệp XSD với tệp XML. Điều này có thể được thay đổi thông qua tab giản đồ được hiển thị:

Hãy kết thúc việc xuất nó và xem nhanh dữ liệu của tệp XML thu được.

Lưu ý rằng các tệp đính kèm được mô tả trong Dữ liệu cây con và nội dung tệp được mã hóa base-64. Hãy thử nhập tệp XML. Sau khi xem qua trình hướng dẫn nhập, chúng ta sẽ nhận được hộp thoại này:

Hãy lưu ý các tính năng sau:

  • Giống như với xuất, chúng tôi có tùy chọn để chuyển đổi XML.
  • Chúng tôi có thể kiểm soát việc nhập cấu trúc, dữ liệu hay cả hai

Nếu sau đó chúng tôi hoàn tất quá trình nhập tệp XML, chúng tôi nhận thấy rằng quá trình này cũng nhanh như thao tác copy’n’paste mà chúng tôi đã thực hiện.

Giờ đây, chúng tôi biết rằng có một con đường tốt hơn để sao chép một số bản ghi có tệp đính kèm. Nhưng trong tình huống này, chúng tôi muốn làm điều này theo chương trình, thay vì tương tác. Chúng ta có thể làm điều tương tự như chúng ta vừa làm không? Một lần nữa, câu trả lời là có. Có nhiều cách để làm điều tương tự nhưng tôi nghĩ phương pháp dễ nhất là sử dụng 3 phương pháp mới đã được thêm vào Application đối tượng kể từ Access 2010:

  • ExportXML phương pháp
  • TransformXML phương pháp
  • ImportXML phương pháp

Lưu ý rằng ExportXML phương thức hỗ trợ xuất từ ​​các đối tượng khác nhau. Tuy nhiên, vì mục tiêu ở đây là có thể sao chép hoặc cập nhật hàng loạt các bản ghi của bảng có trường phần đính kèm, nên loại đối tượng tốt nhất để chúng ta sử dụng là một truy vấn đã lưu. Với một truy vấn đã lưu, chúng tôi có thể kiểm soát những hàng nào nên được chèn hoặc cập nhật và chúng tôi cũng có thể định hình đầu ra. Nếu bạn nhìn vào thiết kế giản đồ của MSysResources bảng bên dưới:

Có một vấn đề tiềm ẩn. Bất cứ khi nào chúng tôi sử dụng chủ đề hoặc hình ảnh, chúng tôi tham chiếu mục đó theo tên, không phải theo ID. Tuy nhiên, Name cột không phải là duy nhất và không phải là khóa chính của bảng. Do đó, khi chúng tôi thêm hoặc cập nhật bản ghi, chúng tôi muốn khớp trên Tên chứ không phải cột Id cột. Điều này có nghĩa là khi chúng tôi xuất, chúng tôi có thể không nên bao gồm Id và chúng tôi chỉ nên xuất danh sách duy nhất của Tên để đảm bảo rằng các tài nguyên không đột ngột chuyển từ “Open.png” sang “Close.png” hoặc điều gì đó ngớ ngẩn.

Sau đó, chúng tôi sẽ tạo một truy vấn để đóng vai trò là nguồn cho các bản ghi mà chúng tôi muốn nhập vào MSysResources bàn. Hãy bắt đầu với SQL này chỉ để chứng minh việc lọc xuống một tập hợp con các bản ghi:

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

Sau đó, chúng tôi sẽ lưu nó dưới dạng qryResourcesExport . Sau đó, chúng tôi có thể viết mã VBA để xuất XML:

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

Điều này mô phỏng quá trình xuất mà chúng tôi đã thực hiện tương tác ban đầu.

Tuy nhiên, nếu sau đó chúng tôi nhập XML kết quả, chúng tôi chỉ có tùy chọn thêm dữ liệu vào một bảng hiện có. Chúng tôi không thể kiểm soát bảng nào nó sẽ thêm vào; nó sẽ tìm một bảng hoặc bảng truy vấn có cùng tên (ví dụ: qryResourcesExport và nối các bản ghi vào truy vấn đó. Nếu truy vấn có thể cập nhật được thì không có vấn đề gì và nó sẽ chèn vào Ví dụ mà truy vấn dựa trên. Nhưng điều gì sẽ xảy ra nếu truy vấn nguồn mà chúng tôi sử dụng không thể cập nhật hoặc có thể không tồn tại? Trong cả hai trường hợp, chúng tôi sẽ không thể nhập tệp XML như hiện tại. Nó có thể không nhập được hoặc không thể tạo một bảng mới có tên qryResourcesExport không giúp được gì cho chúng tôi. Và trường hợp sao chép dữ liệu từ Ví dụ thì sao tới MSysResources ? Chúng tôi không muốn nối dữ liệu vào Ví dụ bảng.

Đó là nơi mà TransformXML phương pháp đến để giải cứu. Một cuộc thảo luận đầy đủ về cách viết một phép chuyển đổi XML nằm ngoài phạm vi nhưng bạn sẽ có thể tìm thấy nhiều tài nguyên về cách viết một biểu định kiểu XSLT để mô tả phép biến đổi. Có một số công cụ trực tuyến bạn cũng có thể sử dụng để xác thực XSLT của mình. Đây là một. Đối với trường hợp đơn giản, chúng tôi chỉ muốn kiểm soát bảng nào tệp XML sẽ nối các bản ghi vào, bạn có thể bắt đầu với tệp XSLT này. Sau đó, bạn có thể chạy mã VBA sau:

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

Chúng tôi có thể thay thế tệp XML ban đầu bằng tệp XML đã được chuyển đổi, tệp này bây giờ sẽ chèn vào MSysResources bảng thay vì vào (có thể là truy vấn / bảng không tồn tại) qryResourcesExport .

Sau đó, chúng tôi cần xử lý các bản cập nhật. Bởi vì chúng tôi thực sự đang thêm các bản ghi mới và MSysResources bảng không có bất kỳ ràng buộc nào đối với các tên trùng lặp, chúng ta cần đảm bảo rằng mọi bản ghi hiện có có cùng tên sẽ bị xóa trước. Điều này có thể được thực hiện bằng cách viết một truy vấn tương đương như sau:

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

sau đó chạy nó trước trước khi chạy mã VBA:

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

Vì tệp XML đã được chuyển đổi, ImportXML bây giờ phương thức sẽ chèn dữ liệu vào MSysResources bảng thay vì truy vấn ban đầu mà chúng tôi đã sử dụng với ExportXML phương pháp. Chúng tôi chỉ định rằng nó sẽ nối dữ liệu vào một bảng hiện có. Tuy nhiên, nếu bảng không tồn tại, nó sẽ được tạo.

Và cùng với đó, chúng tôi đã đạt được cập nhật hàng loạt / chèn bảng với trường tệp đính kèm, nhanh hơn nhiều so với mã VBA tập bản ghi gốc và tập hợp bản ghi con. Hy vọng rằng sẽ giúp! Ngoài ra, nếu bạn cần trợ giúp về việc phát triển các ứng dụng Access, vui lòng liên hệ với chúng tôi!


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. 5 bước để xây dựng cơ sở dữ liệu tiếp thị

  2. Truy cập mô-đun lớp và các lớp gói

  3. Cách hiển thị các mục bị ẩn trong trình duyệt đối tượng VBA

  4. Đặt quyền truy cập cơ sở dữ liệu

  5. Kết nối PHP trên Linux với Microsoft Access trên Windows Share