Transactions Locks «Prev  Next»

Lesson 11 Error testing
Objective Test for Error Handling

Test for Error Handling and Transaction Locking

SQL Server uses two main mechanisms to determine when to roll back transactions during error handling:
  1. SET XACT_ABORT: This is a server configuration setting that controls the transaction behavior on encountering errors.
    • SET XACT_ABORT ON (default): This is the most common setting. Any runtime statement error within a transaction will cause the entire transaction to automatically rollback.
    • SET XACT_ABORT OFF: Errors are reported, but the transaction remains open and must be explicitly rolled back using the `ROLLBACK TRANSACTION` statement. This setting is less common as errors can leave the database in an inconsistent state.
  2. TRY...CATCH Blocks (T-SQL): This is a T-SQL construct that allows you to define a code block and specify how to handle errors that might occur within it.
    • TRY Block: Contains the SQL statements you want to execute as part of the transaction.
    • CATCH Block: Executes if an error occurs within the TRY block. You can include logic here to handle the error gracefully, potentially including a `ROLLBACK TRANSACTION` statement to undo changes.

How They Work Together:
  • SET XACT_ABORT is a global setting that defines the default behavior for all transactions on the server.
  • TRY...CATCH Blocks provide a more granular way to handle errors within specific sections of your code. Even with `SET XACT_ABORT ON`, you can use a `CATCH` block to perform specific actions before the automatic rollback occurs.

Example:
-- Assuming SET XACT_ABORT is ON

BEGIN TRANSACTION;

UPDATE Customers SET Name = 'New Name' WHERE CustomerID = 10;

-- This statement might fail due to a constraint violation
INSERT INTO Orders (CustomerID, OrderDate) VALUES (15, '2023-01-01');

COMMIT TRANSACTION;  -- This won't execute if there's an error

-- Alternatively, handle the error with TRY...CATCH

BEGIN TRANSACTION;

TRY
  UPDATE Customers SET Name = 'New Name' WHERE CustomerID = 10;
  INSERT INTO Orders (CustomerID, OrderDate) VALUES (15, '2023-01-01');
CATCH
  -- Handle the error (e.g., log it or display a message to the user)
  ROLLBACK TRANSACTION; 	
 

Purpose of Error Handling and SQL-Server Business Rules

Error handling is very important within transactions. Without handling errors, you would not know when to roll back transactions. It is important to know that if any statement in your transaction produces a severe error such that the transaction cannot be completed successfully, SQL Server automatically rolls back the transaction.


Testing for errors

Assuming that the transaction was not automatically rolled back, you need to test for errors in your transactions. There are system-level global variables that are populated with values by SQL Server for this purpose. These are the variables that you can use:
  1. @@ERROR:Gives the SQL Server error number of the last executed Transact-SQL statement. If there was no error, @@ERROR will be 0.
  2. @@TRANCOUNT:Indicates the number of currently open transactions for a session. Every call to BEGIN TRANSACTION increments @@TRANCOUNT by 1. END TRANSACTION decrements @@TRANCOUNT by 1. @@ROLLBACK TRANSACTION resets @@TRANCOUNT to 0, unless a savepoint name is specified. In this case, @@TRANCOUNT is unaffected.
  3. @@ROWCOUNT:Indicates the number of rows that are affected by the last executed Transact-SQL statement.

When errors occur, it is a good idea to generate an error message and send it back to the application that started the transaction. This is done by using the RAISERROR Transact-SQL statement. It follows this general syntax:

 MESSAGE_ID is a user defined message number. All user-defined messages are stored in the sysmessages table. The message number should be greater than 50,000. The message stored in the sysmessage table can be formatted, but this formatting is not discussed in this course.
1) MESSAGE_ID is a user defined message number. All user-defined messages are stored in the sysmessages table. The message number should be greater than 50,000. The message stored in the sysmessages table can be formatted.

message string is a string of text to report back to the calling application or procedure. This is used in place of looking up the message text in the sysmessages table. If this option is used, an error message of 50000 is automatically used.
2) message_string is a string of text to report back to the calling application or procedure. This is used in place of looking up the message text in the sysmessages table. If this option is used, an error message of 50000 is automatically used.

severity is the category level of error. The severity can be any of the following:
3) severity is the category level of error. The severity can be any of the following:

State is an arbitrary value that you select from 1 to 127 to further identify the state of your error.
4) State is an arbitrary value that you select (from 1 to 127) to further identify the state of your error.

argument is one or more arguments that can be used in formatting the message. If the message_ID parameter is used.
5) argument is one or more arguments that can be used in formatting the message. If the message_ID parameter is used.

option is one of the following : 1) Logs the error in the application log and the server log 2) NOWAIT: Sends messages immediately (asynchronously) to the client. It does not wait for processing to finish. 3) SETERROR: Set the @@ERROR global variable, regardless of the severity level specified.
6) option is one of the following : 1) Logs the error in the application log and the server log 2) NOWAIT: Sends messages immediately (asynchronously) to the client. It does not wait for processing to finish. 3) SETERROR: Set the @@ERROR global variable, regardless of the severity level specified.

RAISERROR (Transact-SQL)

Applies To: SQL Server 2014, SQL Server 2016 Preview
Generates an error message and initiates error processing for the session. RAISERROR can either reference a user-defined message stored in the sys.messages catalog view or build a message dynamically. The message is returned as a server error message to the calling application or to an associated CATCH block of a
TRY CATCH 
construct. New applications should use THROW instead.
Applies to: SQL Server (SQL Server 2008 through current version), Azure SQL Database, Azure SQL Data Warehouse Public Preview.


Because the RAISERROR Transact-SQL statement takes the last two arguments as the database ID and the database name, you can obtain the current database ID and the current database name by using the DB_ID() and DB_NAME() functions, respectively. However, you cannot use these directly in the RAISERROR Transact-SQL statement, so you must declare variables of the type that are returned by these functions, call those functions, and assign the return values to the variables that you declared. You can do that with this simple code (which you can use consistently in your procedures):
DECLARE @DBID SMALLINT
DECLARE @DBNAME NVARCHAR(128)
SET @DBID = DB_ID()
SET @DBNAME = DB_NAME()

Then you can use the @DBID and @DBNAME variables when needed, like this:
RAISERROR ("Error Text Here", 16, 1, @DBID, @DBNAME)

Testing Transact-SQL Transactions

It is a good idea to implement some form of error handling to test if the individual Transact-SQL statements that are wrapped within the transaction fail. There are as many different ways to do this as you can think of. Here is one example of how you might implement error handling and rollback transactions when errors occur:
DECLARE @DBID SMALLINT
DECLARE @DBNAME NVARCHAR(128)
SET @DBID = DB_ID()
SET @DBNAME = DB_NAME()
BEGIN TRAN
UPDATE Finance
SET Salary = 100000
WHERE EmployeeID = 101
select @@rowcount
select @@trancount
IF @@ROWCOUNT = 0 or @@TRANCOUNT = 0
    BEGIN
 /* an error occurred in the first statement */
 ROLLBACK TRAN
        RAISERROR ("Finance failed - 
  Transaction rolled back",
  16, 1, @DBID, @DBNAME)
        RETURN
    END
UPDATE FinanceHistory
SET LastSalaryUpdate = "07/04/99"
WHERE EmployeeID = 101
IF @@ROWCOUNT = 0 or @@TRANCOUNT = 0
    BEGIN
 /* an error occurred in the second statement */
 ROLLBACK TRAN
        RAISERROR ("FinanceHistory failed - 
  Transaction rolled back", 16, 1, @DBID,
 @DBNAME)
        RETURN
    END
/* everything succeeded - commit the transaction */
COMMIT TRAN

Notice in the code above that not only does the code rollback the transaction if either of the two Transact-SQL statements contained within fail, but a detailed error message is displayed in either case. This way debugging the transaction is easy. This could have been taken one step further by displaying a different error message based on @@ROWCOUNT being 0 (indicating that the condition set in the WHERE clause produced no results) or the @@TRANCOUNT being 0 (indicating that the transaction had already been rolled back or committed by another process). The next lesson reviews the information covered in this module.

Handling Transaction Errors - Exercise

Click the Exercise link to practice handling errors within your transactions.
Handling Transaction Errors - Exercise

SEMrush Software