Catching SQL Script Errors from an SQL Job

When structuring scripts to execute in production, I often wrap a transaction around the contents of my statements. This is done to keep data integrity in-tact and to make things simpler to support in the event that the execute fails. Here is a quick example:

BEGIN TRANSACTION
UPDATE tbl_Employees SET LastName = 'Williams' WHERE Id = 100
UPDATE tbl_EmployeeDepartments SET EmployeeId = 100 WHERE Id = 20
COMMIT TRANSACTION

In this example, I’ve wrapped both UPDATE statements inside a transaction because I only want the data to be committed when both tables are updated successfully. If there were errors in either statement (column constraint issues, etc.), the transaction would remain open and I would have to issue a ROLLBACK TRANSACTION statement to proceed.

To take this to the next level, I make use of TRY/CATCH and will automatically issue a ROLLBACK in case of a statement execution failure.

Consider the scenario:
CREATE TABLE [dbo].[tbl_Employees](
[EmployeeId] [int],
[FirstName] [varchar](150)
[LastName] [varchar](150)
)
CREATE TABLE [dbo].[tbl_EmployeeDepartments](
[DepartmentId] [int],
[EmployeeId] [int]
)

INSERT INTO tbl_Employees VALUES (100, 'Reagan', 'Williams')
INSERT INTO tbl_Departments VALUES (20, 100)

BEGIN TRANSACTION
BEGIN TRY
-- This will succeed by itself
UPDATE tbl_Employees SET LastName = 'W' WHERE EmployeeId = 100

-- This will fail due to the datatype
UPDATE tbl_EmployeeDepartments SET EmployeeId = 'Williams' WHERE DepartmentId = 20

COMMIT TRANSACTION

END TRY
BEGIN CATCH
ROLLBACK TRANSACTION

END CATCH

The query returns a successful execution, even though the 2nd update statement inside the BEGIN/TRY block failed.

The CATCH coupled with the “ROLLBACK TRANSACTION” is what causes this query to execute without error. This is where the RAISERROR function can be used to help identify query problems.

The RAISERROR function allows a string to be passed in the first argument as a message:
RAISERROR('An exception was encountered.', 15, 1)

But I would suggest making use of the functions:
ERROR_MESSAGE()
ERROR_SEVERITY()
ERROR_STATE()

As these can return more meaningful error messages.

Example of how to successfully trap the error message and report it to the results window (for unattended script executions):

DECLARE @ErrorMessage varchar(4000)
DECLARE @ErrorSeverity int
DECLARE @ErrorState int

SET @ErrorState = 0

BEGIN TRANSACTION
BEGIN TRY
-- This will succeed by itself
UPDATE tbl_Employees SET LastName = 'W' WHERE EmployeeId = 100

-- This will fail due to the datatype
UPDATE tbl_EmployeeDepartments SET EmployeeId = 'Williams' WHERE DepartmentId = 20

END TRY

BEGIN CATCH
SELECT
@ErrorMessage = ERROR_MESSAGE(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE()

END CATCH

IF @ErrorState != 0
BEGIN
IF XACT_STATE() != 0
BEGIN
ROLLBACK TRANSACTION
END

RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState)
END
ELSE
BEGIN
IF XACT_STATE() = 1
BEGIN
COMMIT TRANSACTION
END
END

Here is the results output of the SQL Server Agent log:

You can read more about RAISERROR here:
http://msdn.microsoft.com/en-us/library/ms178592.aspx

VN:F [1.9.1_1087]
Rating: 10.0/10 (2 votes cast)
VN:F [1.9.1_1087]
Rating: 0 (from 0 votes)
Catching SQL Script Errors from an SQL Job, 10.0 out of 10 based on 2 ratings
This entry was posted in SQL Server. Bookmark the permalink.

Leave a Reply