The following example demonstrates how to use a transaction in DAO to perform multiple updates in a batch. It uses three pass-through queries in a transaction to transfer money from a savings account to a checking account. The application (the client) sends queries and updates to the server and retrieves the data it needs.
Sub TransferFunds () Dim MyWS As Workspace, MyDB As Database, MyQuery As QueryDef On Error GoTo TransferFailed Set MyWS = DBEngine.Workspaces(0) Set MyDB = MyWS.OpenDatabase("somedb.mdb") MyWS.BeginTrans ' Begin transaction. ' Create temporary pass-through query. Set MyQuery = MyDB.CreateQueryDef("") MyQuery.Connect = "ODBC;DSN=Bank;UID=teller;DATABASE=access" MyQuery.ReturnsRecords = False MyQuery.SQL = "UPDATE Accounts SET Balance = Balance - 100
_WHERE AccountID = 'SMITH_SAV'" MyQuery.Execute ' Subtract from savings account. MyQuery.SQL = "UPDATE Accounts SET Balance = Balance + 100
_WHERE AccountID = 'SMITH_CHK'" MyQuery.Execute ' Add to checking account. MyQuery.SQL = "INSERT INTO LogBook (Type, Source, Destination,
_Amount) VALUES ('Transfer', 'SMITH_SAV', 'SMITH_CHK', 100)" MyQuery.Execute ' Log transaction. MyWS.CommitTrans ' Commit transaction. MyDB.Close Exit Sub TransferFailed: MsgBox Error$ MyWS.Rollback ' If one operation fails, roll them all back. Exit Sub End Sub
Note the use of the CreateQueryDef method to create a temporary pass-through query. This technique introduces the minimum possible overhead for the Jet database engine, while still making use of transactions transparently through Microsoft Basic. However, it limits functionality to what the server provides, and it can't be as easily parameterized.
If your application provides data-entry forms for multiple records of data, such as an order entry system, you can improve performance and robustness by saving new records in a local holding table and then transferring batches of records from the holding table to the server all at once within a transaction.
To insert a batch of records on a server using a transaction:
For example, suppose you have an Orders form containing an Order Details subform based on two local tables — LclOrders and LclOrderDetails. The user enters a new order and a group of detail records in the subform. When the user clicks the Post Records command button on the Orders form, the following procedure is executed:
Sub PostRecords_Click () Dim MyWS As Workspace, MyDB As Database On Error GoTo TransferFailed Set MyWS = DBEngine.Workspaces(0) Set MyDB = MyWS.OpenDatabase("somedb.mdb") MyWS.BeginTrans ' Begin transaction. MyDB.Execute "INSERT INTO RmtOrdersEmpty SELECT *
_FROM LclOrders", dbFailOnError MyDB.Execute "INSERT INTO RmtOrderDetailsEmpty SELECT *
_FROM LclOrderDetails", dbFailOnError MyDB.Execute "DELETE FROM LclOrders" MyDB.Execute "DELETE FROM LclOrderDetails" MyWS.CommitTrans ' Commit transaction. MyDB.Close ... ' Clear form for next order entry. Exit Sub TransferFailed: MsgBox Error$ MyWS.Rollback ' If one operation fails, roll them all back. Exit Sub End Sub
This example clears the data entry form after deleting the records from the local tables to present the user with a blank form for the next entry. In addition, the dbFailOnError option is used with the two append queries so that an error occurs if any part of either query fails. The error handler then rolls back any changes made.
This procedure doesn't insert the records directly into the tables on the server. RmtOrdersEmpty and RmtOrderDetailsEmpty aren't the remote tables; instead, they are the following queries, which are based on the attached remote tables but return no records:
SELECT * FROM RmtOrders WHERE False SELECT * FROM RmtOrderDetails WHERE False
Inserting records into empty queries on attached remote tables provides the fastest possible speed, similar to opening a recordset using the OpenRecordset method with the dbAppendOnly option.