In my Are you ready for SQL Server 2012 or are you still partying like it is 1999? post, I wrote about how you should start using SQL Server 2005 and SQL Server 2008 functionality now in order to prepare for SQL Server 2012. I still see tons of code that is written in the pre 2005 style and people still keep using those functions, procs and statements even though SQL Server 2005 and 2008 have much better functionality.

Today we are going to take a look at TRY CATCH. Like in most modern programming languages, you put your code in the TRY block and you check for the errors in the CATCH block. SQL Server has a bunch of functions that will help you identify why your code failed, here is a list of the functions and what they return

ERROR_NUMBER()
returns the number of the error

ERROR_SEVERITY()
returns the severity of the error

ERROR_STATE()
returns the error state number

ERROR_PROCEDURE()
returns the name of the stored procedure or trigger where the error occurred, this will be NULL if you run an ad-hoc SQL statement

ERROR_LINE()
returns the line number inside the routine that caused the error

ERROR_MESSAGE()
returns the complete text of the error message. The text includes the values supplied for any substitutable parameters, such as lengths, object names, or times

Let’s run an example that generates a divide by zero error, in the catch we are just doing a simple select that calls the functions mentioned before to see what they return

T-SQL
1
2
3
4
5
6
7
8
9
10
11
12
13
BEGIN TRY
    --  divide-by-zero error.
    SELECT 1/0
END TRY
BEGIN CATCH
    SELECT
    ERROR_NUMBER() AS ErrorNumber
    ,ERROR_SEVERITY() AS ErrorSeverity
    ,ERROR_STATE() AS ErrorState
    ,ERROR_PROCEDURE() AS ErrorProcedure
    ,ERROR_LINE() AS ErrorLine
    ,ERROR_MESSAGE() AS ErrorMessage;
END CATCH;
BEGIN TRY
    --  divide-by-zero error.
    SELECT 1/0
END TRY
BEGIN CATCH
    SELECT
    ERROR_NUMBER() AS ErrorNumber
    ,ERROR_SEVERITY() AS ErrorSeverity
    ,ERROR_STATE() AS ErrorState
    ,ERROR_PROCEDURE() AS ErrorProcedure
    ,ERROR_LINE() AS ErrorLine
    ,ERROR_MESSAGE() AS ErrorMessage;
END CATCH;

ErrorNumber	ErrorSeverity	ErrorState	ErrorProcedure	ErrorLine	ErrorMessage
8134	        16	        1	        NULL	        3	       Divide by zero 
                                                                               error encountered.

As you can see we got all that information back, that was pretty nice. Let’s take it to the next step

Create the following table to store all the error information in

T-SQL
1
2
3
4
5
6
7
8
CREATE TABLE LogErrors (ErrorTime datetime,
            ErrorNumber int,
            ErrorSeverity int,
            ErrorState int, 
            ErrorProc nvarchar(100), 
            ErrorLine int, 
            ErrorMessage nvarchar(1000))
GO
CREATE TABLE LogErrors (ErrorTime datetime,
			ErrorNumber int,
			ErrorSeverity int,
			ErrorState int, 
			ErrorProc nvarchar(100), 
			ErrorLine int, 
			ErrorMessage nvarchar(1000))
GO

Create this stored procedure that will insert into the table we just created

T-SQL
1
2
3
4
5
6
7
8
9
10
11
CREATE PROCEDURE prInsertError
AS
INSERT LogErrors
SELECT GETDATE(),
    ERROR_NUMBER(),
    ERROR_SEVERITY(), 
    ERROR_STATE(), 
    ERROR_PROCEDURE(), 
    ERROR_LINE(), 
    ERROR_MESSAGE() ;
GO
CREATE PROCEDURE prInsertError
AS
INSERT LogErrors
SELECT GETDATE(),
    ERROR_NUMBER(),
    ERROR_SEVERITY(), 
    ERROR_STATE(), 
    ERROR_PROCEDURE(), 
    ERROR_LINE(), 
    ERROR_MESSAGE() ;
GO

Run these 3 queries, they will generate 3 inserts into the LogErrors table

T-SQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
BEGIN TRY
    SELECT 1/0
END TRY
BEGIN CATCH
    EXEC prInsertError
END CATCH;
 
 
BEGIN TRY
    SELECT convert(int,'a')
END TRY
BEGIN CATCH
    EXEC prInsertError
END CATCH;
 
 
BEGIN TRY
    SELECT convert(tinyint,300)
END TRY
BEGIN CATCH
    EXEC prInsertError
END CATCH;
BEGIN TRY
    SELECT 1/0
END TRY
BEGIN CATCH
    EXEC prInsertError
END CATCH;


BEGIN TRY
    SELECT convert(int,'a')
END TRY
BEGIN CATCH
    EXEC prInsertError
END CATCH;


BEGIN TRY
    SELECT convert(tinyint,300)
END TRY
BEGIN CATCH
    EXEC prInsertError
END CATCH;

If you check now what is in the table, you will see 3 rows

T-SQL
1
SELECT * FROM LogErrors
SELECT * FROM LogErrors

ErrorTime ErrorNumber ErrorSeverity ErrorState ErrorProc ErrorLine ErrorMessage
2011-12-21 20:08:20.890 8134 16 1 NULL 2 Divide by zero error encountered.
2011-12-21 20:08:22.907 245 16 1 NULL 2 Conversion failed when converting the varchar value ‘a’ to data type int.
2011-12-21 20:08:25.270 220 16 2 NULL 2 Arithmetic overflow error for data type tinyint, value = 300.

That is all for today, as you can see error handling is much better than having to check for @@ERROR after every insert. You can also have 1 stored proc that you can call from everywhere and this proc will log all errors plus any other information you want to capture like user name, host name etc etc

Come back tomorrow for the next post in this series