1.   SQL Server 2000 and lower

1.1.Summary

(adopted from http://sqlmag.com/t-sql/error-handling-sql-server-2005)

1.1.1.  Limited – mainly the @@error function

SQL Server 2000 and earlier editions is limited and cumbersome. Error-handling code isn't structured and many errors aren't trappable.

1.1.2.     Challenges – (1) no structured construct for identifying errors and handling them; (2) while most constraint violations are trappable, but conversion errors, deadlocks, and other errors terminate your batch; (3) No way to test the intentional deadlocks.

Some deadlocks are the result of poor programming or a lack of indexes, but others are intentional. You might design a deadlock to prevent consistency problems, such as lost updates. You'll usually want to handle intentional deadlocks by retrying the transaction. However, because a deadlock is an error that terminates the batch, the retry code never has a chance to run.

 

Below is adopted from http://www.sommarskog.se/error-handling-I.html and http://www.sommarskog.se/error-handling-II.html)

1.2.  Error Message Elements

1.2.1.         Error number,

1.2.2.         Severity level,

1.2.3.         State,

1.2.4.         Procedure,

1.2.5.         Line,

1.2.6.         Message text

1.3.  Detecting Error

1.3.1. @@Error - mainly

1.3.2. A nonzero return value from a SP – some cases

1.3.3. @@rowcount does not return an expected value (e.g., returns more than one row) – some cases

1.4.  What happens when an error occurs? – It depends on the error type and where the error occurs

1.4.1. The default behaviors (i.e., SET XACT_ABORT OFF)

1.4.1.1.          Statement-termination

The current statement is aborted and rolled back. Execution continues on the next statement.

1.4.1.2.          Scope-abortion.

The current scope (stored procedure, user-defined function, or block of loose SQL statements, including dynamic SQL) is aborted, and execution continues on the next statement in the calling scope.

1.4.1.3.          Batch-abortion.

The execution of the entire batch – that is, the block of SQL statements that the client submitted to SQL Server – is aborted.

1.4.1.4.          Connection-termination.

The client is disconnected and any open transaction is rolled back.

However, if you have trigger context when you are in a trigger, or you are in a stored procedure, user-defined function or block of dynamic SQL that has been called directly or indirectly from a trigger. That is, somewhere on the call stack, there is a trigger. If you are in trigger context, all errors terminate the batch and roll back the transaction on the spot. (Connection-terminating errors still terminate the connection, of course.)

1.4.2.     Control Over or Change the Default Error Behaviors

1.4.2.1. SET XACT_ABORT ON - the statement-terminating errors become batch-aborting errors. But not all errors terminate the batch.

Recommendations:

1.4.2.2.          ARITHABORT, ARITHIGNORE and ANSI_WARNINGS (For a very small set of errors such as when a division by zero or an overflow occurs):

o   ON - Batch-abortion

o   OFF -  Warning message, result is NULL

o   ON - No action at all, result is NULL

o   OFF - Warning message, result is NULL

o   ON - Statement-termination

o   OFF- Return NULL

1.5.  Raiserror - The message is returned as a server error message to the calling application (i.e., send the error message to the application) and sets a system flag to record that an error has occurred.

Why needed? If you catch an error on the server, the client will never see the error, unless you call RAISERROR in the error handler. Unfortunately, you cannot re-raise the exact error message, since RAISERROR does not permit you to use error numbers less than 50000.

1.6.  Retrieving the Text of an Error Message

No native supported way to retrieve the full text of an error message in SQL 2000. Need to code it such as the one in http://www.sommarskog.se/errhandle/spGET_LastErrorMessage.sp

1.7.   How to implement error handling strategies in SQL Server 2000

1.7.1.      Use @@Error - For any statement in which an error could affect the result of the stored procedure, or a stored procedure that has called it. Practically for:

1.7.2.     Special Considerations (see the original article for details)

2.   SQL Server 2005 Enhancements

2.1.  Error Detection – TRY…CATCH

Microsoft recommends using the TRY...CATCH construct to handle errors. The TRY...CATCH construct also supports additional system functions (ERROR_LINE, ERROR_MESSAGE, ERROR_PROCEDURE, ERROR_SEVERITY, and ERROR_STATE) that return more error information than @@ERROR. TRY...CATCH also supports an ERROR_NUMBER function that is not limited to returning the error number in the statement immediately after the statement that generated an error.

2.2.  Error Message Catching – using the six built-in error functions

Six functions have been added and can be used in the CATCH block: error_number(), error_severity(), error_state(), error_procedure() , error_line(), and error_message().

 

Note: error_message() is the expanded message with the parameters filled in. In SQL 2000. You can only get a text from master.dbo.sysmessages, which is a placeholder for interesting things like which constraint that was violated. To get the full text of the error message in a proper way, you need a client to pick it up and log it.

 

USE AdventureWorks;

GO

-- Check to see whether this stored procedure exists.

IF OBJECT_ID (N'usp_GetErrorInfo', N'P') IS NOT NULL

              DROP PROCEDURE usp_GetErrorInfo;

GO

-- Create procedure to retrieve error information.

CREATE PROCEDURE usp_GetErrorInfo

AS

   SELECT

       ERROR_NUMBER() AS ErrorNumber

       ,ERROR_SEVERITY() AS ErrorSeverity

       ,ERROR_STATE() AS ErrorState

       ,ERROR_LINE () AS ErrorLine

       ,ERROR_PROCEDURE() AS ErrorProcedure

       ,ERROR_MESSAGE() AS ErrorMessage;

GO

-- SET XACT_ABORT ON will cause the transaction to be uncommittable

-- when the constraint violation occurs.

SET XACT_ABORT ON;

BEGIN TRY

              BEGIN TRANSACTION;

              -- A FOREIGN KEY constraint exists on this table. This

              -- statement will generate a constraint violation error.

              DELETE FROM Production.Product

              WHERE ProductID = 980;

              -- If the DELETE statement succeeds, commit the transaction.

              COMMIT TRANSACTION;

END TRY

BEGIN CATCH

              -- Execute error retrieval routine.

              EXECUTE usp_GetErrorInfo;

              -- Test XACT_STATE:

              -- If 1, the transaction is committable.

              -- If -1, the transaction is uncommittable and should

              --            be rolled back.

              -- XACT_STATE = 0 means that there is no transaction and

              --            a commit or rollback operation would generate an error.

              -- Test whether the transaction is uncommittable.

              IF (XACT_STATE()) = -1

              BEGIN

              PRINT

              N'The transaction is in an uncommittable state.' +

              'Rolling back transaction.'

              ROLLBACK TRANSACTION;

              END;

              -- Test whether the transaction is committable.

              IF (XACT_STATE()) = 1

              BEGIN

              PRINT

              N'The transaction is committable.' +

              'Committing transaction.'

              COMMIT TRANSACTION; 

              END;

END CATCH;

GO

2.3.  Advantages over SQL Server 2000

2.3.1.     SQL Server 2005 lets you trap most errors that terminate a batch in SQL Server 2000

2.3.2.     You get more information about the error SQL Server generates with the six built-in error functions

2.3.3.     More structured and more robust

2.4.  One way to implement error handling with Try-Catch for Transaction Roll Back

(from http://www.codeproject.com/Articles/38650/Overview-of-Error-Handling-in-SQL-Server-2005)

Here I am going to explain one real life scenario of using TRY-CATCH block. One of the common scenarios is usingTransaction. In a Transaction, we can have multiple operations. If all operations executed successfully, then database will commit otherwise we need to ROLLBACK.

/*

  I want to delete a Particular Records from Both Student

  Details and Library. Database will only commit, iff both

  delete statement execute successfully, If fails it will Roll

  back. Intentionally  I have passed a wrong roll ( Which causes)

  the exception and transaction will rollback.

*/

BEGIN TRY

   -- Start A Transaction

   BEGIN TRANSACTION 

 

   -- Delete Student From StudenDetails Table

   DELETE FROM StudentDetails WHERE Roll = '1'

   Print 'Delete Record from Student Details Table'

   -- Delete The Same Student Records From Library Table also

   DELETE FROM Library  WHERE Roll = 'a'

   Print 'Delete Record from Library Table'

   -- Commit if Both Success

   COMMIT

   -- Update Log Details

   Insert into LogDetails(ID,Details) values ('1','Transaction Successful');

END TRY

BEGIN CATCH

Print 'Transaction Failed - Will Rollback'

  -- Any Error Occurred during Transaction. Rollback

  IF @@TRANCOUNT > 0

        ROLLBACK  -- Roll back

END CATCH

3.   SQL Server 2012 Enhancement – THROW replaces Raiserror

(adopted from http://social.technet.microsoft.com/wiki/contents/articles/20002.structured-error-handling-mechanism-in-sql-server-2012.aspx)

3.1.How Raiserror works in SQL Server 7.0 – 2008R2?

The RAISERROR itself needs other elements to identify the error number, error message, etc as below:

Here is sample code to produce the error details:

SET NOCOUNT ON;

BEGIN TRY                         -- Start to try executing statements

          SELECT 1 / 0;               -- Statement

END TRY                           -- End of trying to execute statements

BEGIN CATCH                       -- Start to Handle the error if occurs

          DECLARE @ErrorMessage NVARCHAR(4000);

          DECLARE @ErrorSeverity INT;

          DECLARE @ErrorState INT;

          SELECT

          @ErrorMessage = ERROR_MESSAGE(),

          @ErrorSeverity = ERROR_SEVERITY(),

          @ErrorState = ERROR_STATE();

          RAISERROR (@ErrorMessage, -- Message text.

              @ErrorSeverity, -- Severity.

              @ErrorState -- State.

                );

END CATCH                         -- End of Handling the error if occurred

Msg 50000, Level 16, State 1, Line 19

Divide by zero error encountered.

3.2.  THROW replaces RAISERROR

3.2.1.     Syntax – 3 optional parameters

THROW [ { error_number | @local_variable },

        { message | @local_variable },

        { state | @local_variable } ]

[ ; ]

--  Example 1: with 3 parameters.

 

THROW 51000, 'The record does not exist.', 1;

--  Example 2: without parameters.

 

USE tempdb;

GO

CREATE TABLE dbo.TestRethrow

(             ID INT PRIMARY KEY

);

BEGIN TRY

              INSERT dbo.TestRethrow(ID) VALUES(1);

--  Force error 2627, Violation of PRIMARY KEY constraint to be raised.

              INSERT dbo.TestRethrow(ID) VALUES(1);

END TRY

BEGIN CATCH

              PRINT 'In catch block.';

              THROW;

END CATCH;

3.2.2.     Differences between RAISERROR and THROW

The following table lists differences between the RAISERROR and THROW statements.

 

RAISERROR statement                    

           THROW statement

If a msg_id is passed to RAISERROR, the ID must be defined in sys.messages.

The error_number parameter does not have to be defined in sys.messages.

The msg_str parameter can contain printf formatting styles.

 

An example of Printf in C:

 

printf("Color %s, number1 %d, number2 %05d, hex %#x, float %5.2f, unsigned value %u.\n","red", 123456, 89, 255, 3.14159, 250);

 

An example in SQL Server

RAISERROR ('%s %d should be between %u and %d.',16, 1, 'The inventory level for Product', 1, 1, 1000)

 The message parameter does not accept printf                       

 style formatting. Instead, you use the 

 FORMATMESSAGE function as below:

The FORMATMESSAGE function constructs a message from an existing message in sys.messages. The functionality of FORMATMESSAGE resembles that of the RAISERROR statement. However, RAISERROR prints the message immediately, while FORMATMESSAGE returns the formatted message for further processing.

 

FORMATMESSAGE ( msg_number , [ param_value [ ,...n ] ] )

The following example shows how to use the FORMATMESSAGE function with THROW to throw a customized error message. The example first creates a user-defined error message by usingsp_addmessage. Because the THROW statement does not allow for substitution parameters in the message parameter in the way that RAISERROR does, the FORMATMESSAGE function is used to pass the three parameter values expected by error message 60000.

 

EXEC sys.sp_addmessage

              @msgnum   = 60000

,@severity = 16

,@msgtext  = N'This is a test message with one numeric parameter (%d), one string parameter (%s), and another string parameter (%s).'

              ,@lang = 'us_english';

GO

DECLARE @msg NVARCHAR(2048) = FORMATMESSAGE(60000, 500, N'First string', N'second string');

THROW 60000, @msg, 1;

Here is the result set.

Msg 60000, Level 16, State 1, Line 2

This is a test message with one numeric parameter (500), one string parameter (First string), and another string parameter (second string). 

The severity parameter specifies the severity of the exception.

There is no severity parameter. The exception severity is always set to 16.

3.3.  What are the benefits of THROW over RAISERROR?

3.3.1.     Correct line number of the error (even when you re-throw the exception with the THROW keyword the original error number and line number is preserved unlike the RAISERROR command where it is overwritten) – not the line number where RAISERROR executed!

3.3.2.     Easy to use - no need for extra code as in RAISERROR

3.3.3.     Complete termination - The severity level raised by THROW is always 16. But the more important feature is that when the THROW statement in a CATCH block is executed, then other code after this statement will never run.

3.3.4.     Independence of sys.messages - This feature makes it possible to re-throw custom message numbers without the need to use sp_addmessage to add the number.

3.4.  One way to implement error handling with TRY/CATCH/THROW in SQL Server 2012 for rollback transactions

CREATE PROC sptest

AS

SET NOCOUNT ON;

BEGIN TRY

 SET XACT_ABORT ON;   --set xact_abort option

 BEGIN TRAN            --begin transaction

              CREATE TABLE dbo.T1

              ( id int );

              SELECT 1/0

 COMMIT TRAN                    --commit transaction

END TRY

BEGIN CATCH

              IF @@TRANCOUNT > 0  --check if there are open transaction?

              ROLLBACK TRAN;  --rollback transaction

              THROW                     

             

END CATCH

go

EXEC sptest;

go

SELECT *

FROM dbo.T1;