This is the first article in the series of articles on Exception Handling in Sql Server. Below is the complete list of articles in this series.
Part I: Exception Handling Basics
Part II: TRY…CATCH (Introduced in Sql Server 2005)
Part III: RAISERROR Vs THROW (Throw: Introduced in Sql Server 2012)
Part IV: Exception Handling Template
Exception Handling Basics
If we are not clear about the basics of Exception handling in Sql Server, then it is the most complex/confusing task, it will become nightmarish job to identify the actual error and the root cause of the unexpected results. In this blog post I will try to make sure that all the concepts are cleared properly and the one who goes through it should feel the sense of something they have learnt new and feel themselves an expert in this area. And at the end of the blog post will present the ideal exception handling template which one should be using for proper error handling in Sql Server.
Last week on 11th January, 2014, I have presented a session on this topic at Microsoft Office in the Sql Bangalore User Group meeting which is attend by hundreds of enthusiastic Sql Server working professionals. Received very good feedback and few messages posted in the Facebook SQLBangalore user group were “Thanks Basavaraj Biradar! Your session was divine!” By Community Member Adarsh Prasad, “Thanks Basavaraj for your excellent session” By Community Member Selva Raj. Enough Self Praise, enough expectation is set, let’s cut short the long story short and move onto the deep dive of Exception handling basics
In this topic will cover the following concepts with extensive list of examples
- Error Message
- Error Actions
Error Message |
Let’s start with a simple statement like below which results in an exception as I am trying to access a non-existing table.
--------------Try To Access Non-Existing Table --------------- SELECT * FROM dbo.NonExistingTable GO
Result of the above query:
Msg 208, Level 16, State 1, Line 2
Invalid object name ‘dbo.NonExistingTable’.
By looking at the above error message, we can see that the error message consists of following 5 parts:
Msg 208 – Error Number
Level 16 – Severity of the Error
State 1 – State of the Error
Line 2 – Line Number of the statement which generated the Error
Invalid object name ‘dbo.NonExistingTable’. – Actual Error Message
Now Let’s wrap the above statement which generated the error into a stored procedure as below
---Create the Stored Procedure CREATE PROCEDURE dbo.ErrorMessageDemo AS BEGIN SELECT * FROM dbo.NonExistingTable END GO --Execute the Stored Procedure EXEC dbo.ErrorMessageDemo GO
Result of executing the above stored procedure is:
Msg 208, Level 16, State 1, Procedure ErrorMessageDemo, Line 4
Invalid object name ‘dbo.NonExistingTable’.
If we compare this error message with the previous error message, then this message contains one extra part “Procedure ErrorMessageDemo“ specifying the name of the stored procedure in which the exception occurred.
Parts of ErrorMessage
The below image explains in detail each of the six parts of the error message which we have identified just above:
In case the image is not clear below is the detail which I have tried to present in it:
ERROR NUMBER/ Message Id:
Any error number which is <= 50000 is a System Defined Messages and the ones which are > 50000 are User Defined Messages. SYS.Messages catalog view can be used to retrieve both System and User Defined Messages. We can add a user defined message using sp_addmessage and we can remove it using the system stored procedure sp_dropmessage.
ERROR SEVERITY: Error Severity can be between 0-25.
0-10: Informational or a warning
11-16: Programming Errors
17-25: Resource / Hardware / OS/ Sql Server Internal Errors
20-25: Terminates the Connection
19-25: Only User with SysAdmin rights can raise error’s with this severity
ERROR STATE: Same Error can be raised for several different conditions in the code. Each specific condition that raises the error assigns a unique state code. Also the SQL Support Team uses it to find the location in the source code where that error is being raised.
ERROR PROCEDURE: Name of the Stored Procedure or the Function in which the Error Occurred. It Will be blank if it is a Normal Batch of Statement.
ERROR LINE: Line Number of the Statement within SP/ UDF/ Batch which triggered the error. It will be 0 If SP/UDF Invoke Causes the Error.
ERROR MESSAGE: Error description detailing out the reason for the error
Error Actions |
Now let us see how Sql Server Reacts to different errors. To demonstrate this let us create a New Database and table as shown below:
--Create a New database for the Demo CREATE DATABASE SqlHintsErrorHandlingDemo GO USE SqlHintsErrorHandlingDemo GO CREATE TABLE dbo.Account ( AccountId INT NOT NULL PRIMARY KEY, Name NVARCHAR (50) NOT NULL, Balance Money NOT NULL CHECK (Balance>=0) ) GO
As the Account table has Primary Key on the AccountId column, so it will raise an error if we try to duplicate the AccountId column value. And the Balance column has a CHECK constraint Balance>=0, so it will raise an exception if the value of Balance is <0.
Let us first check whether we are able to insert valid Account into the Account table.
INSERT INTO dbo.Account(AccountId, Name , Balance) VALUES(1, 'Account1', 10000)
Result: We are able to successfully insert a record in the Account table
Now try to insert one more account whose AccountId is same as the one which we have just inserted above.
INSERT INTO dbo.Account(AccountId, Name , Balance) VALUES(1, 'Duplicate', 10000)
Result: It fails with below error message, because we are trying to insert a duplicate value for the the Primary Key column AccountId.
Msg 2627, Level 14, State 1, Line 1
Violation of PRIMARY KEY constraint ‘PK__Account__349DA5A67ED5FC72’. Cannot insert duplicate key in object ‘dbo.Account’. The duplicate key value is (1).
The statement has been terminated.
Let me empty the Account Table by using the below statement:
DELETE FROM dbo.Account
DEMO 1: Now let us see what will be the result if we execute the below batch of Statements:
INSERT INTO dbo.Account(AccountId, Name , Balance) VALUES(1, 'Account1', 10000) INSERT INTO dbo.Account(AccountId, Name , Balance) VALUES(1, 'Duplicate', 10000) INSERT INTO dbo.Account(AccountId, Name , Balance) VALUES(2, 'Account2', 20000) GO
Result: The First and Third Insert statements in the batch are succeeded even though the Second Insert statement fails
From the above example result it is clear that even though the Second insert statement is raising a primary key voilation error, Sql server continued the execution of the next statement and it has successfully inserted the Account with AccountId 2 by the third Insert statement.
If Sql Server terminates the statement which raised the error but continues to execute the next statements in the Batch. Then such a behavior by a Sql Server in response to an error is called Statement Termination.
Let me clear the Account Table by using the below statement before proceeding with the Next DEMO:
DELETE FROM dbo.Account
DEMO 2: Now let us see what will be the result if we execute the below batch of Statements. The only difference between this batch of statement and the DEMO 1 is the first line i.e. SET XACT_ABORT ON:
SET XACT_ABORT ON INSERT INTO dbo.Account(AccountId, Name , Balance) VALUES(1, 'Account1', 10000) INSERT INTO dbo.Account(AccountId, Name , Balance) VALUES(1, 'Duplicate', 10000) INSERT INTO dbo.Account(AccountId, Name , Balance) VALUES(2, 'Account2', 20000) GO
RESULT: Only the first Insert succeeded
From the above example result it is clear that failure in the Second insert statement due to primary key violation caused Sql Server to terminate the execution of the Subsequent statements in the batch.
If Sql Server terminates the statement which raised the error and the subsequent statements in the batch then such behavior is termed as Batch Abortion.
The Only difference in the DEMO 2 script from DEMO 1 is the additional first statement SET XACT_ABORT ON. So from the result it is clear that the SET XACT_ABORT ON statement is causing Sql Server to do the Batch Abortion for a Statement Termination Error. It means SET XACT_ABORT ON converts the Statement Terminating errors to the Batch Abortion errors.
Let me clear the Account Table and also reset the Transaction Abort setting by using the below statement before proceeding with the Next DEMO :
DELETE FROM dbo.Account SET XACT_ABORT OFF GO
DEMO 3: Now let us see what will be the result if we execute the below batch of Statements. The only difference between this batch of statement and the DEMO 1 is that the INSERT statements are executed in a Transaction:
BEGIN TRAN INSERT INTO dbo.Account(AccountId, Name , Balance) VALUES(1, 'Account1', 10000) INSERT INTO dbo.Account(AccountId, Name , Balance) VALUES(1, 'Duplicate', 10000) INSERT INTO dbo.Account(AccountId, Name , Balance) VALUES(2, 'Account2', 20000) COMMIT TRAN GO
RESULT: Same as the DEMO 1, that is only the statement which raised the error is terminated but continues with the next statement in the batch. Here First and Third Inserts are Successful even though the Second statement raised the error.
Let me clear the Account Table by using the below statement before proceeding with the Next DEMO :
DELETE FROM dbo.Account GO
DEMO 4: Now let us see what will be the result if we execute the below batch of Statements. The only difference between this batch of statement and the DEMO 2 is that the INSERT statement’s are executed within a Transaction
SET XACT_ABORT ON BEGIN TRAN INSERT INTO dbo.Account(AccountId, Name , Balance) VALUES(1, 'Account1', 10000) INSERT INTO dbo.Account(AccountId, Name , Balance) VALUES(1, 'Duplicate', 10000) INSERT INTO dbo.Account(AccountId, Name , Balance) VALUES(2, 'Account2', 20000) COMMIT TRAN GO
From the above example result it is clear that SET XACT_ABORT ON setting not only converts the Statement Termination Errors to the Batch Abortion Errors and also ROLLS BACK any active transactions started prior to the BATCH Abortion errors.
Let me clear the Account Table and also reset the Transaction Abort setting by using the below statement before proceeding with the Next DEMO :
DELETE FROM dbo.Account SET XACT_ABORT OFF GO
DEMO 5: As a part of this DEMO we will verify what happens if a CONVERSION Error occurs within a batch of statement.
CONVERSION ERROR: Trying to convert the string ‘TEN THOUSAND’ to MONEY Type will result in an error. Let us see this with an example:
SELECT CAST('TEN THOUSAND' AS MONEY)
RESULT:
Msg 235, Level 16, State 0, Line 1
Cannot convert a char value to money. The char value has incorrect syntax.
Now let us see what happens if we come across such a CONVERSION error within a batch of statement like the below one:
INSERT INTO dbo.Account(AccountId, Name , Balance) VALUES(1, 'Account1', 10000) UPDATE dbo.Account SET Balance = Balance + CAST('TEN THOUSAND' AS MONEY) WHERE AccountId = 1 INSERT INTO dbo.Account(AccountId, Name , Balance) VALUES(2, 'Account2', 20000) GO
RESULT: Only the First INSERT is successful
From the above result it is clear that CONVERSION errors cause the BATCH abortion, i.e Sql Server terminates the statement which raised the error and the subsequent statements in the batch. Where as PRIMARY KEY violation was resulting in a Statement Termination as explained in the DEMO 1.
Let me clear the Account Table by using the below statement before proceeding with the Next DEMO :
DELETE FROM dbo.Account GO
DEMO 6: Now let us see what will be the result if we execute the below batch of Statements. The only difference between this batch of statement and the previous DEMO 5 is that the Batch statement’s are executed within a Transaction
BEGIN TRAN INSERT INTO dbo.Account(AccountId, Name , Balance) VALUES(1, 'Account1', 10000) UPDATE dbo.Account SET Balance = Balance + CAST('TEN THOUSAND' AS MONEY) WHERE AccountId = 1 INSERT INTO dbo.Account(AccountId, Name , Balance) VALUES(2, 'Account2', 20000) COMMIT TRAN GO
From the above example result it is clear that CONVERSION errors results in a BATCH Abortion and BATCH Abortion errors ROLLS BACK any active transactions started prior to the BATCH Abortion error.
Let me clear the Account Table and also reset the Transaction Abort setting by using the below statement before proceeding with the Next DEMO :
DELETE FROM dbo.Account SET XACT_ABORT OFF GO
Enough examples, let me summarize the Sql Server Error Actions. Following are the four different ways Sql Server responds(i.e. Error Actions) in response to the errors:
- Statement Termination
- Scope Abortion
- Batch Abortion
- Connection Termination
Many of these error actions I have explained in the above DEMOs using multiple examples. To explain these error actions further let us take a scenario as shown in the below image, in this scenario from client system an Execution request for the MainSP is submitted and the MainSP internally calls to sub sp’s SubSP1 and SubSP2 one after another:
Statement Termination :
If Sql Server terminates the statement which raised the error but continues to execute the next statements in the Batch. Then such a behavior by a Sql Server in response to an error is called Statement Termination. As shown in the below image the Statement-1 in SubSP1 is causing an error, in response to this Sql Server terminates only the statement that raised the error i.e. Statement-1 but continues executing subsequent statements in the SubSP1 and MainSP calls the subsequent SP SubSp2.
Scope Abortion :
If Sql Server terminates the statement which raised the error and the subsequent statements in the same scope, but continues to execute all the Statements outside the scope of the statement which raised the error. As shown in the below image the Statement-1 in SubSP1 is causing an error, in response to this Sql Server terminates not only the statement that raised the error i.e. Statement-1, but also terminates all the subsequent statements in the SubSP1, but continues executing further all the statements/Sub Sp’s (For Example SubSP2) in the MainSP.
Let us see this behavior with stored procedures similar to the one explained in the above image. Let us execute the below script to create the three stored procedures for this demo:
-------------Scope Abortion Demo------------- -------Create SubSP1--------- CREATE PROCEDURE dbo.SubSP1 AS BEGIN PRINT 'Begining of SubSP1' --Try to access Non-Existent Table SELECT * FROM NonExistentTable PRINT 'End of SubSP1' END GO -------Create SubSP2--------- CREATE PROCEDURE dbo.SubSP2 AS BEGIN PRINT 'Inside SubSP2' END GO -------Create MainSP--------- CREATE PROCEDURE dbo.MainSP AS BEGIN PRINT 'Begining of MainSP' EXEC dbo.SubSP1 EXEC dbo.SubSP2 PRINT 'End of MainSP' END GO
Once the above stored procedures are created, let us execute the MainSP by the below statement and verify the result:
EXEC dbo.MainSP GO
From the above SP execution results it is clear that the Access for a non existent table NonExistentTable from SubSP1 is not only terminating the statement which try’s to access this NonExistentTable table, but also the Subsequent statements in the SubSP1’s scope. But Sql Server continues with the execution of the subsequent statements which are present in the in the MainSP which has called this SubSP1 and also the SubSP2 is called from the MainSP.
Let us drop all the Stored Procedures created in this demo by using the below script:
DROP PROCEDURE dbo.SubSP2 DROP PROCEDURE dbo.SubSP1 DROP PROCEDURE dbo.MainSP GO
Batch Abortion :
If Sql Server terminates the statement which raised the error and the subsequent statements in the batch then such behavior is termed as Batch Abortion. As shown in the below image the Statement-1 in SubSP1 is causing an error, in response to this Sql Server terminates not only the statement that raised the error i.e. Statement-1, but also terminates all the subsequent statements in the SubSP1 and it will not execute the further statements/Sub Sp’s (For Example SubSP2) in the MainSP. Batch Abortion Errors ROLLS BACK any active transactions started prior to the statement which causes BATCH Abortion error.
We have already seen multiple Batch Abortion examples in the above DEMOs. Here let us see this behavior with stored procedures similar to the one explained in the above image. Let us execute the below script to create the three stored procedures for this demo:
------------Batch Abortion Demo -------------- -------Create SubSP1--------- CREATE PROCEDURE dbo.SubSP1 AS BEGIN PRINT 'Begining of SubSP1' PRINT CAST('TEN THOUSAND' AS MONEY) PRINT 'End of SubSP1' END GO -------Create SubSP2--------- CREATE PROCEDURE dbo.SubSP2 AS BEGIN PRINT 'Inside SubSP2' END GO -------Create MainSP--------- CREATE PROCEDURE dbo.MainSP AS BEGIN PRINT 'Begining of MainSP ' EXEC dbo.SubSP1 EXEC dbo.SubSP2 PRINT 'End of MainSP ' END GO
Once the above stored procedures are created, let us execute the MainSP by the below statement and verify the result:
EXEC dbo.MainSP GO
From the above SP execution results it is clear that the CONVERSION/CAST statement in the SubSP1 is causing the Batch Abortion.It is not only terminating the statement which raised the error but all the subsequent statement in the SubSP1 and the further statement in the MainSP which has called this SubSP1 and also the SubSP2 is not called from the MainSP post this error.
Let us drop all the Stored Procedures created in this demo by using the below script:
DROP PROCEDURE dbo.SubSP2 DROP PROCEDURE dbo.SubSP1 DROP PROCEDURE dbo.MainSP GO
Connection Termination :
Errors with severity level 20-25 causes the Connection Termination. Only User with SysAdmin rights can raise error’s with these severity levels. As shown in the below image the Statement-1 in SubSP1 is causing an error with severity 20-25, in response to this Sql Server terminates not only the statement that raised the error i.e. Statement-1, but also terminates all the subsequent statements in the SubSP1 and it will not execute the further statements/Sub Sp’s (For Example SubSP2) in the MainSP. And finally terminates the connection. Note if there are any active Transactions which are started prior to the statement which caused the Connection Termination error, then Sql Server Takes care of Rolling Back all such transactions.
If we use RaiseError with WITH LOG option to raise an exception with severity level >=20 will result in a connection termination. Let us execute the below statement and observe the result:
RAISERROR('Connection Termination Error Demo', 20,1) WITH LOG GO
RESULT: Connection is Terminated
Below query gives the list of Error’s that cause the Connection Termination.
SELECT * FROM sys.messages WHERE severity >= 20 and language_id =1033
Clean-UP:
Let us drop the database which we have created for this demo
--Drop the Database SqlHintsErrorHandlingDemo USE TempDB GO DROP DATABASE SqlHintsErrorHandlingDemo
Let us know your feedback on this post, hope you have learnt something new. Please correct me if there are any mistakes in this post, so that I can correct it and share with the community.
I think you should give more information about the Scope abortion.
Although it looks similar to a Batch abortion, it seems that when a transaction is added, its behavior is similar to the Statement abortion – the transaction is not being rolled back implicitly, unless a ‘SET XACT_ABORT ON’ was declared.
Also, you listed the ‘Trying to access a non existent table’ under the batch abortion errors list, while, according to your example, it should be under a scope abortion error list (which doesn’t exist at all).
Where did you get the list of the different errors per abortion types? Does Microsoft have any documents about it?
Thanks AP for taking your valuable time and pointing out your observations. I have correctedthe Batch Abortion Image.
Basically, I took couple of error messages from sys.message and tried to simulate these errors and observed how sql server behaves. Based on this experiment classified and presented here. Whenever I get time will experiment few more common errors and classify them accordingly and also will include few more examples for scope abortion.
It would be nice if the readers of this blog contribute from their side too, so that we can come up with exhaustive list which helps to the community…
Hello! Do you use Twitter? I’d like to follow you if that would be okay.
I’m undoubtedly enjoying your blog and look forward to
new updates.
What are transactions?
fantastic explanation… whenever i am trying to read the new concept i used to choose this link and in the first read itself i can able to grasp the exact concept also it is getting stored in mind… really thank you… many peoples are having knowledge’s but the way of presenting or delivering message makes others to understand…… you are great…
once again thank you..
Great article.
Very much clear after looking into this article.. Thanks you very much for sharing the information..Excellent .. hope you post more articles like this.
Thank you Srinivas
Learning from the basics gives us very good foundation ….The way you described the types and source of errors are really good thanks a lot for sharing your knowledge .Keep on going with all concepts hope this will be good source for us to learn more and more …….Thanks once again
good article… thank you!
There’s certainly a great deal to find out about this issue.
I really like all the points you have made.
Nice one..
Thank you Ashish
Hi Basavaraj..!!
while browsing, I found this article and i start reading casually for basic info. But you provide the info which is awesome.I could n’t go out without leaving a comment for you..
Thank you Rakhi
Thanks, really clear things for me. Great Article.
Thanks again
Thank you AM.. Appreciate your comments…
Great explanation, and helpful examples. Thanks for posting this series – I’m going to share with my team.
Thank you Luke… Appreciate your comments…
At the start you presented a session on this topic at Microsoft Office in the Sql Bangalore User Group meeting and got appreciation. You really deserve it. Nice article ever read on Error. Thank you for sharing.
Thank you Amit…
This is my first time pay a visit at here and i am really
impressed to read all at alone place.
Thank you Sir,
Sir Could Explain what is query Optimization ? and how can do this.
Thank you Sir, Very much useful article
I come from Taiwan, your post is helpful for me, thank you.