-
DBEngine và Workspace trong việc truyền tải dữ liệu giữa hai Database
Xuân Thanh > 08-08-18, 04:38 PM
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 -
RE: DBEngine và Workspace trong việc truyền tải dữ liệu giữa hai Database
NguyenDungAnh > 08-08-18, 10:22 PM
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 -
RE: DBEngine và Workspace trong việc truyền tải dữ liệu giữa hai Database
Xuân Thanh > 09-08-18, 12:20 PM
(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 -
RE: DBEngine và Workspace trong việc truyền tải dữ liệu giữa hai Database
NguyenDungAnh > 09-08-18, 06:06 PM
ok bác để em kiểm tra xem -
RE: DBEngine và Workspace trong việc truyền tải dữ liệu giữa hai Database
ongke0711 > 09-08-18, 09:25 PM
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(Thang, Nam)
Dim sPath As String
Dim sSQL As String
Dim StartDate As Date, StopDate As Date
sPath = "\\Mac\Home\Downloads\DeMo Khoa So\LuuDuLieu.accdb" 'Chinh lai duong dan'
StartDate = DateSerial(Nam, Thang, 1)
StopDate = DateSerial(Nam, Thang + 1, 1) - 1
strSQL = "INSERT INTO tblLuuDuLieu IN '" & sPath & "' SELECT * FROM tblNhatKy WHERE Ngay>=" & Format(StartDate,conJetDate) & " AND Ngay <=" & Format(StopDate, conJetDate)
Debug.Print strSQL
CurrentDb.Execute strSQL, dbFailOnError
End Sub
-
RE: DBEngine và Workspace trong việc truyền tải dữ liệu giữa hai Database
NguyenDungAnh > 11-08-18, 11:41 AM
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 -
RE: DBEngine và Workspace trong việc truyền tải dữ liệu giữa hai Database
Xuân Thanh > 11-08-18, 11:49 AM
(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(Thang, Nam)
Dim sPath As String
Dim sSQL As String
Dim StartDate As Date, StopDate As Date
sPath = "\\Mac\Home\Downloads\DeMo Khoa So\LuuDuLieu.accdb" 'Chinh lai duong dan'
StartDate = DateSerial(Nam, Thang, 1)
StopDate = DateSerial(Nam, Thang + 1, 1) - 1
strSQL = "INSERT INTO tblLuuDuLieu IN '" & sPath & "' SELECT * FROM tblNhatKy WHERE Ngay>=" & Format(StartDate,conJetDate) & " AND Ngay <=" & Format(StopDate, conJetDate)
Debug.Print strSQL
CurrentDb.Execute strSQL, dbFailOnError
End Sub
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 -
RE: DBEngine và Workspace trong việc truyền tải dữ liệu giữa hai Database
ongke0711 > 12-08-18, 06:51 PM
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
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...)
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 Nothing) Then
'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.Description, vbExclamation, "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) -
RE: DBEngine và Workspace trong việc truyền tải dữ liệu giữa hai Database
Xuân Thanh > 12-08-18, 08:29 PM
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