Command | Description |
---|---|
BEGIN TRANSACTION | Sets the starting point of an explicit transaction |
ROLLBACK TRANSACTION | Restores original data modified by a transaction, and brings data back to the state it was in at the start of the transaction.Resources held by the transaction are freed. |
COMMIT TRANSACTION | Ends the transaction if no errors were encountered and makes changes permanent. Resources held by the transaction are freed. |
BEGIN DISTRIBUTED TRANSACTION | Allows you to define the beginning of a distributed transaction to be managed by Microsoft Distributed Transaction Coordinator (MS DTC). MS DTC must be running locally and remotely. |
SAVE TRANSACTION | SAVE TRANSACTION issues a savepoint within a transaction,which allows one to define a location to which a transactioncan return if part of the transaction is cancelled. A transaction must be rolled back or committed immediately after rolling back to a savepoint. |
@@TRANCOUNT | Returns the number of active transactions for the connection. BEGIN TRANSACTION increments @@TRANCOUNT by 1, and ROLLBACK TRANSACTION and COMMIT TRANSACTION decrements @@TRANCOUNT by 1. ROLLBACK TRANSACTION to a savepoint has no impact. |
This recipe’s example demonstrates how to use explicit transactions to commit or rollback the data
modification depending on the return of an error in a batch of statements:
===================================================================
-- Before count
SELECT COUNT(*) BeforeCount FROM HumanResources.Department
-- Variable to hold the latest error integer value
DECLARE @Error int
BEGIN TRANSACTION
INSERT HumanResources.Department
(Name, GroupName)
VALUES ('Accounts Payable', 'Accounting')
SET @Error = @@ERROR
IF (@Error<> 0) GOTO Error_Handler
INSERT HumanResources.Department
(Name, GroupName)
VALUES ('Engineering', 'Research and Development')
SET @Error = @@ERROR
IF (@Error <> 0) GOTO Error_Handler
COMMIT TRAN
Error_Handler:
IF @Error <> 0
BEGIN
ROLLBACK TRANSACTION
END
-- After count
SELECT COUNT(*) AfterCount FROM HumanResources.Department
===================================================================
BeforeCount
-----------
18
(1 row(s) affected)
(1 row(s) affected)
Msg 2601, Level 14, State 1, Line 14
Cannot insert duplicate key row in object 'HumanResources.Department'
➥ with unique index 'AK_Department_Name'.
The statement has been terminated.
AfterCount
-----------
18
(1 row(s) affected)
How It Works
The first statement in this example validated the count of rows in the HumanResources.Department
table, returning 18 rows:
-- Before count
SELECT COUNT(*) BeforeCount FROM HumanResources.Department
A local variable is created to hold the value of the @@ERROR function (which captures the latest
error state of a SQL statement):
-- Variable to hold the latest error integer value
DECLARE @Error int
Next, an explicit transaction was started:
BEGIN TRANSACTION
The next statement attempted an INSERT into the HumanResources.Department table. There was
a unique key on the department name, but because the department name didn’t already exist in the
table, the insert succeeded:
INSERT HumanResources.Department
(Name, GroupName)
VALUES ('Accounts Payable', 'Accounting')
Next was an error handler for the INSERT:
SET @Error = @@ERROR
IF (@Error <> 0) GOTO Error_Handler
This line of code evaluates the @@ERROR function. The @@ERROR system function returns the last
error number value for the last executed statement within the scope of the current connection. The
IF statement says that if an error occurs, the code should jump to (using GOTO) the Error_Handler
section of the code.
GOTO is a keyword that helps you control the flow of statement execution. The identifier after
GOTO, Error_Handler, is a user-defined code section.
Next, another insert is attempted, this time for a department that already exists in the table.
Because the table has a unique constraint on the name column, this insert will fail:
INSERT HumanResources.Department
(Name, GroupName)
VALUES ('Engineering', 'Research and Development')
The failure will cause the @@ERROR following this INSERT to be set to a non-zero value. The IF
statement will then evaluate to TRUE, which will invoke the GOTO, thus skipping over the COMMIT TRAN
to the Error_Handler section:
SET @Error = @@ERROR
IF (@Error <> 0) GOTO Error_Handler
COMMIT TRAN
Following the Error_Handler section is a ROLLBACK TRANSACTION:
Error_Handler:
IF @Error <> 0
BEGIN
ROLLBACK TRANSACTION
END
Another count is performed after the rollback, and again, there are only 18 rows in the database.
This is because both INSERTs were in the same transaction, and one of the INSERTs failed. Since
a transaction is all-or-nothing, no rows were inserted:
-- After count
SELECT COUNT(*) AfterCount FROM HumanResources.Department
Some final thoughts and recommendations regarding how to handle transactions in your
Transact-SQL code or through your application:
• Keep transaction time short as possible for the business process at hand. Transactions that
remain open can hold locks on resources for an extended period of time, which can block
other users from performing work or reading data (see later on in the chapter for a review of
locking and blocking).
• Add only relevant Transact-SQL statements to a transaction. Don’t add extra lookups or
updates that are not germane to the specific transaction. Executing SELECT statement within
a transaction can create locks on the referenced tables, which can in turn block other
users/sessions from performing work or reading data.
• Do not open new transactions that require user or external feedback within the transaction.
Open transactions can hold locks on resources, and user feedback can take an indefinite
amount of time to receive. Instead, gather user feedback before issuing an explicit transaction.
No comments:
Post a Comment