How we achieve ERROR HANDLING in sql server?
Read below points:
THROW
RAISE ERROR
TRY_ CATCH
NESTED TRY CATCH
@@TRANCOUNT
XACT_STATE
XACT_ABORT
What is Exception handling in SQL Server?
Some errors are generated by the application at runtime. These errors can be generated because of various reasons and the application is not able to handle these errors, causing the program to break and throw an exception.
Usually, these are generated because of unexpected human input such as passing an unexpected data type input e.g. passing an integer at the place of a string value, dividing a number by zero, etc.
But in real-life scenarios, the program is not supposed to be broken down, but execute flawlessly. Therefore, there is a need for a mechanism that can handle the situations that affect the program’s normal execution or cause it to break. This mechanism is called exception handling or error handling.
Error handling in SQL Server gives us control over the Transact-SQL code. For example, when things go wrong, we get a chance to do something about it and possibly make it right again. SQL Server error handling can be as simple as just logging that something happened, or it could be us trying to fix an error. It can even be translating the error in SQL language because we all know how technical SQL Server error messages could get making no sense and hard to understand. Luckily, we have a chance to translate those messages into something more meaningful to pass on to the users, developers, etc.
SQL Server also provides the exception handling mechanism like any other programming language. There are different ways to handle the exceptions. We will discuss these in the upcoming sections with the help of examples.
Consider the following program which divides the two number:
USE [master]
GO
DECLARE
@Number1 int,
@Number2 int,
@Result real
SET @Number1=10
SET @Number2=0
SET @Result=@Number1/@Number2
PRINT(@Result)
We know that dividing a number by 0 results in infinity. But let us see how the program executes it:Types of exceptions in SQL Server
There are two types of exceptions in SQL Server. These are:
- System Defined Exceptions
- User-Defined Exceptions
System Defined Exceptions: These are the exceptions that are generated by the system during the program execution. The error messages are predefined in the system and the final user will see the predefined message on the console.
User-Defined Exceptions: These are the exceptions that are thrown explicitly by the programmer. The programmer can raise exceptions at any part of the SQL code according to the logic he has implemented in the program.
The programmer can also create custom messages that he finds will be interactive and meaningful to the user. He can also store these errors into log tables to maintain a log of generated exceptions.
Raising Errors with RAISERROR
RAISERROR allows applications to generate an error that could then be caught by the calling process. This makes error handling in the application easier as it is sent like any other system error. RAISERROR can therefore be used to troubleshoot T-SQL codes, debug and check the value of variables and return meaningful error messages based on variables data.
Below is an example of using RAISERROR in SQL Server:
1RAISERROR (N'This is message.', -- Message text.
210, -- Severity,
31); -- Second argument.
4GO
The output would then be as follows:
This is message.
Raising Errors with THROW
The THROW statement is a simpler method of raising errors in T-SQL code.
Below is an example of how THROW is used in SQL Server:
THROW
251000, -- error number
3'This is not a valid value for unit price.', -- message
41; --state
5GO
The result set would then be as follows:
1Msg 51000, Level 16, State 1, Line 1
2This is not a valid value for unit price.
In the example above, 51000 is the error number. The error number is an integer that must be a value between 50000 and 2147483647. The next parameter is the message, which is a string containing a description of the error. It's format is nvarchar(2048), and finally we have the state, which is a constant between 0 and 255. It shows the state to associate with the message. State is of type tinyint.
Understanding the @@Error variable
@@ERROR is a system variable that holds the error number of the last error that has occurred. One of the drawbacks of using @@ERROR is that the value it holds resets as each additional statement is executed. To get the last error number, the query below is used:
Select @@ERROR
Example of Using @@Error
Consider the example below where a custom error is raised before selecting the value of @@ERROR.
1RAISERROR(N'Message', 16, 1);
2IF @@ERROR <> 0
3PRINT 'Error=' + CAST(@@ERROR AS VARCHAR(8));
4GO
The output is as follows:
1Msg 50000, Level 16, State 1, Line 1
2Message
3Error=0
Therefore, when working with @@ERROR, it is recommended to capture the error number into a variable as soon as it occurs and then continue processing with the variable. This is demonstrated in the example below.
1DECLARE @Value int;
2RAISERROR(N'Message', 16, 1);
3SET @Value = @@ERROR;
4IF @Value <> 0
5PRINT 'Error=' + CAST(@Value AS VARCHAR(8));
The output will then be as below, where the ID of the message is successfully captured in the variable @Value.
1Msg 50000, Level 16, State 1, Line 2
2Message
3Error=50000
Try Catch in SQL Server
- We put the SQL statements that may cause an exception in the BEGIN TRY block.
- If any exception arises, the control is immediately transferred to the BEGIN CATCH block.
- In the BEGIN CATCH block, we write the statements that should be executed if an exception is occurred in the BEGIN TRY block. That means the statements inside the BEGIN CATCH block are used for handling the flow of the program in case the exception is triggered.
- If no exception arises i.e. all the SQL statements inside the BEGIN TRY block are executed, the control is not transferred to the BEGIN CATCH block.
Now let us see the general syntax for BEGIN TRY and BEGIN CATCH block.
- Code language: SQL (Structured Query Language) (sql)
BEGIN TRY -- statements that may cause exceptions END TRY
Then you use aBEGIN CATCH...END CATCH
block immediately after theTRY
block:Code language: SQL (Structured Query Language) (sql)BEGIN CATCH -- statements that handle exception END CATCH
The following illustrates a complete
TRY CATCH
construct:Code language: SQL (Structured Query Language) (sql)BEGIN TRY -- statements that may cause exceptions END TRY BEGIN CATCH -- statements that handle exception END CATCH
If the statements between the
TRY
block complete without an error, the statements between theCATCH
block will not execute. However, if any statement inside theTRY
block causes an exception, the control transfers to the statements in theCATCH
block.The
CATCH
block functionsInside the
CATCH
block, you can use the following functions to get the detailed information on the error that occurred:ERROR_LINE()
returns the line number on which the exception occurred.ERROR_MESSAGE()
returns the complete text of the generated error message.ERROR_PROCEDURE()
returns the name of the stored procedure or trigger where the error occurred.ERROR_NUMBER()
returns the number of the error that occurred.ERROR_SEVERITY()
returns the severity level of the error that occurred.ERROR_STATE()
returns the state number of the error that occurred.
Note that you only use these functions in the
CATCH
block. If you use them outside of theCATCH
block, all of these functions will returnNULL
.Nested
TRY CATCH
constructsYou can nest
TRY CATCH
construct inside anotherTRY CATCH
construct. However, either aTRY
block or aCATCH
block can contain a nestedTRY CATCH
, for example:Code language: SQL (Structured Query Language) (sql)BEGIN TRY --- statements that may cause exceptions END TRY BEGIN CATCH -- statements to handle exception BEGIN TRY --- nested TRY block END TRY BEGIN CATCH --- nested CATCH block END CATCH END CATCH
SQL Server
TRY CATCH
examplesFirst, create a stored procedure named
usp_divide
that divides two numbers:Code language: SQL (Structured Query Language) (sql)CREATE PROC usp_divide( @a decimal, @b decimal, @c decimal output ) AS BEGIN BEGIN TRY SET @c = @a / @b; 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 END; GO
In this stored procedure, we placed the formula inside the
TRY
block and called theCATCH
block functionsERROR_*
inside theCATCH
block.Second, call the
usp_divide
stored procedure to divide 10 by 2:Code language: SQL (Structured Query Language) (sql)DECLARE @r decimal; EXEC usp_divide 10, 2, @r output; PRINT @r;
Here is the output
Code language: SQL (Structured Query Language) (sql)5
Because no exception occurred in the
TRY
block, the stored procedure completed at theTRY
block.Third, attempt to divide 20 by zero by calling the
usp_divide
stored procedure:Code language: SQL (Structured Query Language) (sql)DECLARE @r2 decimal; EXEC usp_divide 10, 0, @r2 output; PRINT @r2;
The following picture shows the output:
Because of division by zero error which was caused by the formula, the control was passed to the statement inside the
CATCH
block which returned the error’s detailed information.SQL Serer
TRY CATCH
with transactionsInside a
CATCH
block, you can test the state of transactions by using theXACT_STATE()
function.- If the
XACT_STATE()
function returns -1, it means that an uncommittable transaction is pending, you should issue aROLLBACK TRANSACTION
statement. - In case the
XACT_STATE()
function returns 1, it means that a committable transaction is pending. You can issue aCOMMIT TRANSACTION
statement in this case. - If the
XACT_STATE()
function return 0, it means no transaction is pending, therefore, you don’t need to take any action.
It is a good practice to test your transaction state before issuing a
COMMIT TRANSACTION
orROLLBACK TRANSACTION
statement in aCATCH
block to ensure consistency.Using
TRY CATCH
with transactions exampleFirst, set up two new tables
sales.persons
andsales.deals
for demonstration:Code language: SQL (Structured Query Language) (sql)CREATE TABLE sales.persons ( person_id INT PRIMARY KEY IDENTITY, first_name NVARCHAR(100) NOT NULL, last_name NVARCHAR(100) NOT NULL ); CREATE TABLE sales.deals ( deal_id INT PRIMARY KEY IDENTITY, person_id INT NOT NULL, deal_note NVARCHAR(100), FOREIGN KEY(person_id) REFERENCES sales.persons( person_id) ); insert into sales.persons(first_name, last_name) values ('John','Doe'), ('Jane','Doe'); insert into sales.deals(person_id, deal_note) values (1,'Deal for John Doe');
Next, create a new stored procedure named
usp_report_error
that will be used in aCATCH
block to report the detailed information of an error:Code language: SQL (Structured Query Language) (sql)CREATE PROC usp_report_error 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
Then, develop a new stored procedure that deletes a row from the
sales.persons
table:Code language: SQL (Structured Query Language) (sql)CREATE PROC usp_delete_person( @person_id INT ) AS BEGIN BEGIN TRY BEGIN TRANSACTION; -- delete the person DELETE FROM sales.persons WHERE person_id = @person_id; -- if DELETE succeeds, commit the transaction COMMIT TRANSACTION; END TRY BEGIN CATCH -- report exception EXEC usp_report_error; -- Test if 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 if the transaction is committable. IF (XACT_STATE()) = 1 BEGIN PRINT N'The transaction is committable.' + 'Committing transaction.' COMMIT TRANSACTION; END; END CATCH END; GO
In this stored procedure, we used the
XACT_STATE()
function to check the state of the transaction before performingCOMMIT TRANSACTION
orROLLBACK TRANSACTION
inside theCATCH
block.After that, call the
usp_delete_person
stored procedure to delete the person id 2:Code language: SQL (Structured Query Language) (sql)EXEC usp_delete_person 2;
There was no exception occurred.
Finally, call the stored procedure
usp_delete_person
to delete person id 1:Code language: SQL (Structured Query Language) (sql)EXEC usp_delete_person 1;
The following error occurred:
In this tutorial, you have learned how to use the SQL Server
TRY CATCH
construct to handle exceptions in stored procedures.
Throw custom exception in SQL Server stored procedure
In SQL Server, you can also throw custom exceptions. Throwing a custom exception means you can cause an exception to encounter an error at any part of your code according to your logic.
We use the THROW statement or the RAISERROR function for throwing a custom exception. Both the functions do almost the same work. However, there are some differences between the two.
- The RAISERROR was introduced in SQL Server 7.0 and the THROW statement was introduced in the SQL Server 2012. Microsoft suggests using the THROW statement as it is easier to use and has more functionalities than the RAISERROR function.
- The RAISERROR function keeps the record of only the last exception thrown. However, it is not the case with the THROW statement. The THROW statement keeps the record of all the recent exceptions.
- You can also use the THROW function without using any parameters specified. But, you cannot do this with the RAISERROR function.
- The THROW statement threw an error. It takes three parameters:
- Error number, which should be always greater than or equal to 50000 and less than the INT range.
- Error message, which you want to show to the user. It should be enough informative to the user.
- Error state, the current state of the error. It can have values between 0 to 255 as it supports the TINYINT data type.
- You can also use the THROW() function without any parameter to throw an exception and transfer the control to the CATCH block, but there must be a CATCH block to handle the exception.
Note
A TRY..CATCH block combination catches all the errors that have a severity between 11 and 19.
The CATCH block is executed only if there is an error occurs in T-SQL statements within TRY block otherwise the CATCH block is ignored.
Each TRY block is associated with only one CATCH block and vice versa
TRY and CATCH blocks can’t be separated with the GO statement. We need to put both TRY and CATCH blocks within the same batch.
TRY..CATCH blocks can be used with transactions. We check the number of open transactions by using @@TRANCOUNT function in Sql Server.
XACT_STATE function within the TRY..CATCH block can be used to check whether an open transaction is committed or not. It will return -1 if the transaction is not committed else returns 1.
Post a Comment
0 Comments