Đánh giá chủ đề:
  • 0 Votes - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
DBEngine và Workspace trong việc truyền tải dữ liệu giữa hai Database
#1
1/ Lời dẫn
Chào các bạn!
Trên Forum cũng có nhiều bài viết về DAO. Hôm nay xin gửi tới các bạn một phương thức mới(mà cũ vì cái này tôi đã áp dụng cách nay khoảng 20 năm về trước khi còn dùng Access 2.0 và Access 97). Đó là sử dụng DBEngine và Workspace. Vậy DBEngine và Workspace là gì?
Đối tượng DBEngine là một thuộc tính của đối tượng Application Access và đại diện cho đối tượng cấp cao nhất trong mô hình DAO. Đối tượng DBEngine chứa tất cả các đối tượng khác trong phân cấp đối tượng DAO, nhưng không giống như nhiều đối tượng DAO khác, bạn không thể tạo các đối tượng DBEngine bổ sung
Workspace là một không gian làm việc của DB mà ở đó ta có thể tạo ra nhiều không gian làm việc để có thể làm việc cùng một lúc.Ta có thể hiểu nôm na là DBEngine giông như một ngôi nhà và ỏ trong đó có nhiều phòng(Workspace). Ta có thể đưa các vật dụng(dữ liệu) vào nhiều phòng một lúc và cũng có thê di chuyển đồ dùng từ phòng này qua phòng khác mà không sợ đụng chạm giữa các đồ vật
Tôi sẽ trở lại với Workspace trong một chủ đề khác


2/ Nói về DeMo
Trong DeMo này vừa nhăm giới thiệu về DBEngine và Workspace đồng thời vừa trả lời bạn NguyenDungAnh hỏi về khóa sổ không cho cập nhật dữ liêu sau khi khóa sổ. Link bài viết ở đây http://thuthuataccess.com/forum/thread-10612.html
Trong DeMo tôi giả lập một dự án Sổ Quỹ đơn giản, chỉ có các Table, Form và Modul cần thiết. Các bạn tải DeMo đính kèm và mở các Table, Form và Modul để xem
Chú ý : Các bạn tải DeMo đính kèm và giải nén vào ổ đĩa cứng. File LuuDuLieu các bạn có thể để chung thư mục với file DeMo hoặc khác thư mục. Trước khi vận hành thử, các bạn chỉnh lại đường dẫn đên file LuuDuLieu theo đúng trên máy của các bạn. Khóa sổ được thực hiện qua frmKhoaSo và không cho cập nhật thêm được thực hiện qua txtNgay.AfterUpdate() của frmPhieuThuChi


Mong các bạn cùng trao đổi thêm
Thân mến
Xuân Thanh


File đính kèm
.zip   DeMo Khoa So.zip (Kích cỡ: 55.87 KB / Tải về: 23)
Chữ ký của Xuân Thanh Trăm năm trước thì ta chưa gặp
Trăm năm sau biết gặp được không?
Cuộc đời sắc sắc không không
Thì thôi ta cứ hết lòng vì nhau
ღღღღღTài sản của Xuân Thanh (View All Items) ღღღღღ
Reply
Những người đã cảm ơn NguyenDungAnh , thegioi2018
#2
Cám ơn bác đã chia xẻ kinh nghiệm, em có nhập thử data vào mấy bảng đó nhưng khi bấm khóa sổ thì mở mục lưu dữ liệu vẫn trắng, em dùng office 2013 64 bit
Chữ ký của NguyenDungAnh Đến với cuộc đời hai tay trắng
Giã từ trần thế trắng hai tay
Bao nhiêu tiếng nói tiếng cười
Sống ở cuộc đời lãi được vậy thôi
ღღღღღTài sản của NguyenDungAnh (View All Items) ღღღღღ
Reply
Những người đã cảm ơn
#3
(08-08-18, 10:22 PM)NguyenDungAnh Đã viết: Cám ơn bác đã chia xẻ kinh nghiệm, em có nhập thử data vào mấy bảng đó nhưng khi bấm khóa sổ thì mở mục lưu dữ liệu vẫn trắng, em dùng office 2013 64 bit

1/ Đã chỉnh lại đương dẫn trong hàm CopyDLDaTa chưa?
2/ Tôi không có Office 2013 nên không test được. Có thể là ở 64bit

Bạn thử thế này xem nhé. Mở một modul trống, gõ DBEngine rồi ghi dấu (.) nếu nó xòe ra một danh sách cho bạn chọn Workspace thì mọi chuyện vẫn ổn và kiểm tra ở chỗ khác

Tôi làm trên Office2010 32bit ok mà
Thân mến
Chữ ký của Xuân Thanh Trăm năm trước thì ta chưa gặp
Trăm năm sau biết gặp được không?
Cuộc đời sắc sắc không không
Thì thôi ta cứ hết lòng vì nhau
ღღღღღTài sản của Xuân Thanh (View All Items) ღღღღღ
Reply
Những người đã cảm ơn
#4
ok bác để em kiểm tra xem
Chữ ký của NguyenDungAnh Đến với cuộc đời hai tay trắng
Giã từ trần thế trắng hai tay
Bao nhiêu tiếng nói tiếng cười
Sống ở cuộc đời lãi được vậy thôi
ღღღღღTài sản của NguyenDungAnh (View All Items) ღღღღღ
Reply
Những người đã cảm ơn
#5
Trước đây cũng có một bài của maidinhdan cũng gần với cái cách dùng DBEngine về việc Append dữ liệu giữa 2 file Access khác nhau. Các bạn cũng có thể tham khảo thêm.
Link: http://thuthuataccess.com/forum/thread-9572.html

Tôi cũng đóng góp 1 cách khác:
- Không cần khai báo Database cần backup cho DBEngine Collection.
- Dùng câu lệnh SQL để Insert dữ liệu lên table đích. Vì câu lệnh SQL luôn xử lý nhanh hơn DAO Recordset. "Insert Into..." nó cập nhật một lúc nguyên một bó records (batch update), còn đối với DAO Recordset thì nó Loop qua từng record để update.
  
    INSERT INTO tên Table IN 'đường dẫn file database đích để lưu' SELECT * FROM Table WHERE ...

- Vì dùng câu lệnh SQL nên biến ngày/tháng phải chỉnh lại theo định dạng "mm/dd/yyyy" để lọc dữ liệu cho đúng. 
- Tạo 1 cái Sub tạm đặt tên là CopyDLData2 (). Code như bên dưới. Tạo nút lệnh thực thi tương tự của bác Xuân Thanh.

Mã PHP:
Option Compare Database
Option Explicit

Public Const conJetDate "\#mm\/dd\/yyyy\#" 

Mã PHP:
Sub CopyDLDaTa2(ThangNam)

   Dim sPath As String
   Dim sSQL 
As String
   Dim StartDate 
As DateStopDate As Date
    
   sPath 
"\\Mac\Home\Downloads\DeMo Khoa So\LuuDuLieu.accdb"    'Chinh lai duong dan'
   StartDate DateSerial(NamThang1)
   StopDate DateSerial(NamThang 11) - 1
   
   strSQL 
"INSERT INTO tblLuuDuLieu IN '" sPath "' SELECT * FROM tblNhatKy WHERE Ngay>=" Format(StartDate,conJetDate) & " AND Ngay <=" Format(StopDateconJetDate)
   Debug.Print strSQL
   CurrentDb
.Execute strSQLdbFailOnError

End Sub 

[Hình: 43041230725_2e80ba6644_o.png]
Chữ ký của ongke0711 If you BORN poor, it's not your mistake. But if you DIE poor, It's your mistake!
ღღღღღTài sản của ongke0711 (View All Items) ღღღღღ
Reply
Những người đã cảm ơn maidinhdan , NguyenDungAnh
#6
Bác nói rõ hơn cách insert query vào table được không em chưa hiểu rõ cái này
Chữ ký của NguyenDungAnh Đến với cuộc đời hai tay trắng
Giã từ trần thế trắng hai tay
Bao nhiêu tiếng nói tiếng cười
Sống ở cuộc đời lãi được vậy thôi
ღღღღღTài sản của NguyenDungAnh (View All Items) ღღღღღ
Reply
Những người đã cảm ơn
#7
(09-08-18, 09:25 PM)ongke0711 Đã viết: Trước đây cũng có một bài của maidinhdan cũng gần với cái cách dùng DBEngine về việc Append dữ liệu giữa 2 file Access khác nhau. Các bạn cũng có thể tham khảo thêm.
Link: http://thuthuataccess.com/forum/thread-9572.html

Tôi cũng đóng góp 1 cách khác:
- Không cần khai báo Database cần backup cho DBEngine Collection.
- Dùng câu lệnh SQL để Insert dữ liệu lên table đích. Vì câu lệnh SQL luôn xử lý nhanh hơn DAO Recordset. "Insert Into..." nó cập nhật một lúc nguyên một bó records (batch update), còn đối với DAO Recordset thì nó Loop qua từng record để update.
  
    INSERT INTO tên Table IN 'đường dẫn file database đích để lưu' SELECT * FROM Table WHERE ...

- Vì dùng câu lệnh SQL nên biến ngày/tháng phải chỉnh lại theo định dạng "mm/dd/yyyy" để lọc dữ liệu cho đúng. 
- Tạo 1 cái Sub tạm đặt tên là CopyDLData2 (). Code như bên dưới. Tạo nút lệnh thực thi tương tự của bác Xuân Thanh.

Mã PHP:
Option Compare Database
Option Explicit

Public Const conJetDate "\#mm\/dd\/yyyy\#" 

Mã PHP:
Sub CopyDLDaTa2(ThangNam)

   Dim sPath As String
   Dim sSQL 
As String
   Dim StartDate 
As DateStopDate As Date
    
   sPath 
"\\Mac\Home\Downloads\DeMo Khoa So\LuuDuLieu.accdb"    'Chinh lai duong dan'
   StartDate DateSerial(NamThang1)
   StopDate DateSerial(NamThang 11) - 1
   
   strSQL 
"INSERT INTO tblLuuDuLieu IN '" sPath "' SELECT * FROM tblNhatKy WHERE Ngay>=" Format(StartDate,conJetDate) & " AND Ngay <=" Format(StopDateconJetDate)
   Debug.Print strSQL
   CurrentDb
.Execute strSQLdbFailOnError

End Sub 

[Hình: 43041230725_2e80ba6644_o.png]

1/ Chính xác là như vậy. Nếu dùng lệnh Insert thì đâu cần dùng tới DAo DBEngine. Úp thẳng nó vô Table là OK
Nhưng đây đang nói về DAO nên phải dùng Do...Loop. Nếu không dùng DAO thì dùng SQL để úp thẳng vô

2/ Sory maidinhdan vì chưa đọc bài của bạn nên post bài này
Chữ ký của Xuân Thanh Trăm năm trước thì ta chưa gặp
Trăm năm sau biết gặp được không?
Cuộc đời sắc sắc không không
Thì thôi ta cứ hết lòng vì nhau
ღღღღღTài sản của Xuân Thanh (View All Items) ღღღღღ
Reply
Những người đã cảm ơn maidinhdan
#8
Anh Xuân Thanh đã nói về cái DBEngine nên sẳn đây tôi cũng nói thêm chút về:
        
         DBEngine(0)(0) và CurrentDb

[Hình: 43980246221_74f4419923_z.jpg]

Như ví dụ của anh Xuân Thanh ở trên dùng: DBEngine.Workspaces (0).OpenDatabase (sPath) để mở một file .mdb khác với file .mdb mình đang thao tác. Từ đó bạn thấy được rằng DBEngine.Workspaces( ) là một Database Collections, bạn có thể nạp các database (.mdb, .accdb) vô cái Workspaces() rồi gọi nó theo số thứ tự. 
VD: DBEngine.Workspaces (0).Database(1) là cái database bên ngoài (thứ 2) cần mở để lưu dữ liệu (là file luudulieu.accdb trong ví dụ trên)

Còn CurrentDb() mà chúng ta cũng hay dùng khi khai báo: Set db = CurrentDb chính là cái ứng dụng Access mà ta đang xử lý trên cái giao diện (UI) của nó (có các ngăn Table, Query, Form, Report, Module).

Vậy cái DBEngine.Workspaces(0).Database(0) sẽ trả về chính là database mà ta đang thao tác và cũng là database trả về của hàm CurrentDb().
DBEngine.Workspaces (0).Database(0) còn có khác viết khác cho gọn là: DBEngine (0)(0)
(cái ký hiệu mảng (0)(0) này mà dùng font chữ Georgia nhìn cũng hay hay... 014 )

Bài viết dưới đây chỉ nói về tham chiếu đến cái Database hiện tại (đang thao tác) tức là về DBEngine(0)(0) và CurrentDB().
Việc tham chiếu đến Database bên ngoài thì dùng DBEngine(0).OpenDatabase (pathDB) là cách không phải bàn cãi.

DBEngine(0)(0) và CurrentDb() cũng có điểm khác nhau.

* Database trả về có được cập nhật hay không?

CurrentDb() là một hàm Access. Nó được Access (trên giao diện ứng dụng) xử lý để tham chiếu tới cái database hiện thời và tạo một bản sao database của nó. Mỗi khi được gọi, CurrentDb() sẽ luôn tự động Refresh các bộ (collection) Form, Query nên luôn trả về 1 bản sao database mới và đã được cập nhật mới nhất (các form, query... mới thêm vào).

Trong khi đó DBEngine(0)(0) tham chiếu thẳng đến cái DAO (Data Access Object) của bộ máy JET nên cái database được tham chiếu này chưa hẳn đã được cập nhật các đối tượng như form, query. Nói dễ hiểu là nó sẽ trả về cái Database với các bộ Table, Form, Query...ngay khi Ms Access mở file .mdb. Sau đó nếu bạn có thiết kế thêm Table, Query thì cái database của DBEngine(0)(0) này sẽ chưa có chúng trong đó.  Muốn cái DBEngine(0)(0) này được cập nhật mới nhất thì mỗi khi gọi phải Refresh các bộ (Collections) table, query...mà việc Refresh này cũng ảnh hưởng đến tốc độ thực thi không ít.
          DBEngine(0)(0).QueryDefs.Refresh

* Tốc độ thực thi:

Nếu không cần sử dụng cái database được cập nhật mới nhất (không cần Refresh) thì DBEngine(0)(0) sẽ cho tốc độ xử lý nhanh hơn gấp nhiều lần so với dùng CurrentDb(). 
(có những trường hợp test nhanh hơn 17 - 60 lần)

Như đã nó ở trên, khi gọi CurrentDb() thì hàm này sẽ tự động refresh các forms, query collection và trả về là 1 bản sao của cái database mới vừa được Refresh -> do đó nó sẽ phải dùng thêm bộ nhớ (memory) cho tác vụ này. Ngược lại thì DBEngine(0)(0) tham chiếu thẳng tới cái database đang có sẳn trong bộ nhớ khi Access mở file, không dùng thêm bộ nhớ cho bản copy database. 

Vậy nếu đọc những đều trên thì chúng ta sẽ suy nghĩ là sau này nên dùng DBEngine(0)(0) thay cho CurrentDb() trong những tác vụ mà không cần cái database cập nhật (chẳng hạn như: Insert thêm dòng vào table...). Nhưng DBEngine(0)(0) có một vấn đề là: trong vài trường hợp nó chưa chắc chỉ đến cái database hiện thời mà bạn đang thao tác, viết code. Một cái là database mà Ms Access đang xử lý trên giao diện ứng dụng của nó, một cái là database mà bộ máy JET của Access đang xử lý. (JET gọi nôm na là bộ não của Ms Access). Lỗi này xảy ra khi Access chạy các Form, report... wizards, từ đó có thể gây ra cái database hiện thời không nằm trong vị trí đầu tiên (0) của Workspace nữa. Muốn gán database hiện thời vào vị trí đầu tiên của Workspaces thì phải chạy lệnh Refresh.
         DBEngine(0).Databases.Refresh

Do đó cách an toàn nhất vẫn là dùng CurrentDb() để tránh tham chiếu sai database cũng như database không được cập nhật các table, query mới thêm vào. Bạn có thể mở bao nhiêu cái database tuỳ ý và đóng chúng lại khi không cần dùng nữa.


Một số lưu ý khi dùng CurrentDb và DBEngine(0)(0):

- Đóng database sau khi xử lý.

Mã PHP:
Dim db As DAO.Database

Set db
=CurrentDB
...

'db.Close  <=Đóng hoặc không đóng cái "db" này cũng không sao'
Set db=Nothing 

Mã PHP:
Dim db As DAO.Database

Set db
=DBEngine(0)(0)
...

'db.Close  <=Không nên đóng cái "db" này tránh Access có thể bị crash'
Set db=Nothing 

Mã PHP:
Dim db As DAO.Database

Set db
=DBEngine(0).OpenDatabase (sPath)
...

db.Close  '<=Nên đóng cái "db" này vì nó là database bên ngoài'
Set db=Nothing 


- Ngoài ra khi dùng CurrentDb(), bạn nên Set một biến đối tượng cho nó, không nên dùng trực tiếp vì sẽ có trường hợp báo lỗi.
Ví dụ:
'Code lỗi:
    Dim tbl As DAO.TableDef
    Set tbl = CurrentDb.TableDefs(0) ==> đối tượng database sẽ mất sau dòng này.
    Debug.Print tbl.Name

--> Code trên sẽ báo lỗi vì cái đối tượng database hiện tại sẽ mất đi ngay khi cái hàm CurrentDb được thực thi xong-> dòng lệnh kế tiếp "Debug.Print tbl.Name" sẽ không chạy được.

'Code không bị lỗi:
    Dim db AS DAO.Database
    Dim tbl As DAO.TableDef
    Set db = CurrentDb
    Set tbl = db.TableDefs(0)
    Debug.Print tbl.Name


--> biến đối tượng database "db" không mất đi cho đến khi bạn đóng nó: db.Close

---------------------------------------------------------------

Từ những điểm mạnh yếu của DBEngine (0)(0) và CurrentDb(), người ta đã viết ra cái hàm khác thay thế để tận dụng tốc độ của DBEngine (không cần Refresh các table, query, form... collections)  và việc trả về đúng cái database hiện thời của hàm CurrentDb.

- Hàm bên dưới do David-W-Fenton viết. Bạn copy nó vào Module đặt tên tuỳ ý.

Mã PHP:
Option Compare Database
Option Explicit

Public Function dbLocal(Optional blnCleanup As Boolean False) As DAO.Database

   On Error 
GoTo errHandler
   Static dbCurrent 
As DAO.Database

   If blnCleanup Then 
GoTo closeDB

retryDB
:
   If dbCurrent Is Nothing Then
       Set dbCurrent 
CurrentDb()
   End If

exitRoutine:
   Set dbLocal dbCurrent
   Exit 
Function

closeDB:
   If Not (dbCurrent Is NothingThen
       
'dbCurrent.close ' this never has any effect
       Set dbCurrent 
Nothing
   End 
If
   GoTo exitRoutine

errHandler
:
   Select Case Err.Number
   Case 3420    
' Object invalid or no longer set.'
       Set dbCurrent Nothing
       If Not blnCleanup Then
           Resume retryDB
       Else
           Resume closeDB
       End 
If
   Case Else
       MsgBox Err.Number ": " Err.DescriptionvbExclamation"Error in dbLocal()"
       Resume exitRoutine
   End Select
   
End 
Function 


- Sử dụng trong ứng dụng:
  + Không cần khai báo: Dim db DAO.Database, Set db =...
  + Gọi trực tiếp:
             Set rs = dbLocal.OpenRecordSet ("strSQL...")
     hoặc
             dbLocal.Execute (strSQL)
  + Chú ý khi đóng ứng dụng thì nên gọi:
             Call dbLocal (False)
  + Khi có thêm các query, table vào ứng dụng, cái biến tĩnh database của bạn đã lưu trên cache không được cập nhật nên bạn phải refresh đối tượng database.
             dbLocal.QueryDefs.Refresh
        
-----------------------------------------------------------

Tóm lại:

- Dùng DBEngine.Workspaces.OpenDatabase ("đường dẫn tới file mdb") để tham chiếu tới database bên ngoài cái giao diện database đang thao tác.

- Dùng DBEgine(0)(0) hoặc CurrentDb() đều tham chiếu tới database hiện tại nhưng DBEngine sẽ xử lý nhanh hơn so với CurrentDb nhưng database sẽ không được cập nhật và có thể trong vài trường hợp không tham chiếu đến database hiện tại.

- Giải pháp hàm dbLocal() của David-W-Fenton tích hợp ưu điểm tốc độ của DBEngine và khắc phục việc tham chiếu sai tới database hiện thời.




(Nguồn st stackoverflow.com)
Chữ ký của ongke0711 If you BORN poor, it's not your mistake. But if you DIE poor, It's your mistake!
ღღღღღTài sản của ongke0711 (View All Items) ღღღღღ
Reply
Những người đã cảm ơn Xuân Thanh , maidinhdan
#9
Tốc độ xử lý sẽ rất nhanh khi dùng hàm dbLocalI() thay cho CurrentDb(). Ngày xưa tôi hay dùng cái này nhưng viết ngắn hơn nên phải dùng thêm cái lệnh close nó
Thank you
Chữ ký của Xuân Thanh Trăm năm trước thì ta chưa gặp
Trăm năm sau biết gặp được không?
Cuộc đời sắc sắc không không
Thì thôi ta cứ hết lòng vì nhau
ღღღღღTài sản của Xuân Thanh (View All Items) ღღღღღ
Reply
Những người đã cảm ơn


Có thể liên quan đến chủ đề
Chủ đề: Tác giả Trả lời: Xem: Bài mới nhất
  Khóa phím close trong Access Noname 3 2,614 26-09-18, 04:07 PM
Bài mới nhất: tranthanhan1962
  [Thủ Thuật] DeMo dùng ADODB để kết nối dữ liệu Excel và Access Xuân Thanh 9 590 18-09-18, 12:14 PM
Bài mới nhất: duynamvnn1208
  Hướng dẫn cơ bản về việc lập báo cáo tồn kho và in thẻ kho bằng query Xuân Thanh 33 23,826 01-08-18, 10:00 AM
Bài mới nhất: Popeye
  [Help] Giá trị trùng trong report vulhu06 1 636 14-07-18, 09:03 PM
Bài mới nhất: hoaqldd33
  [Hỏi] Câu lệnh đóng tất cả các tab đang mở trong access mrtoanbin 3 281 29-05-18, 06:20 PM
Bài mới nhất: ongke0711

Chuyển nhanh:


User(s) browsing this thread: 1 Guest(s)
Diễn Đàn Thơ Văn Thi Ẩm Lâu|Nhà Hàng Sông Thơ| PMA Nha Trang| Gỗ Acrylic Không Đường Line