{"id":228,"date":"2007-02-20T00:00:00","date_gmt":"2007-02-20T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/sql-server-error-handling-workbench\/"},"modified":"2021-09-29T16:22:22","modified_gmt":"2021-09-29T16:22:22","slug":"sql-server-error-handling-workbench","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/t-sql-programming-sql-server\/sql-server-error-handling-workbench\/","title":{"rendered":"SQL Server Error Handling Workbench"},"content":{"rendered":"<p>Error handling in SQL Server breaks down into two very distinct situations: you&#8217;re handling errors because you&#8217;re in SQL Server 2005 or you&#8217;re not handling errors because you&#8217;re in SQL Server 2000.  What&#8217;s worse, not all errors in SQL Server, either version, can be handled. I&#8217;ll specify where these types of errors come up in each  version.<\/p>\n<p>The different types of error handling will be addressed in two  different sections. &#8216;ll be using two different databases for the  scripts as well, <code>[pubs]<\/code> for SQL Server 2000 and <code>[AdventureWorks]<\/code> for SQL Server 2005.<\/p>\n<p>I&#8217;ve broken down the scripts and descriptions into sections. Here is a Table of Contents to allow you to quickly move to the piece of code you&#8217;re interested in. Each piece of code will lead with the server  version on which it is being run. In this way you can find the section and the code you want quickly and easily.<\/p>\n<p>As always, the intent is that you load this workbench into Query Analyser or Management Studio and try it out for yourself! The workbench script is available in the downloads at the bottom of the article. <\/p>\n<p><code><\/p>\n<ul>\n<li>\n\t\t<a href=\"#first\">GENERATING AN ERROR<\/a>\n\t<\/li>\n<li>\n\t\t<a href=\"#second\">SEVERITY AND EXCEPTION TYPE<\/a>\n\t<\/li>\n<li>\n\t\t<a href=\"#third\">TRAP AN ERROR<\/a>\n\t<\/li>\n<li>\n\t\t<a href=\"#fourth\">USING RAISERROR<\/a>\n\t<\/li>\n<li>\n\t\t<a href=\"#fifth\">RETURNING ERROR CODES FROM STORED PROCEDURES<\/a>\n\t<\/li>\n<li>\n\t\t<a href=\"#sixth\">TRANSACTIONS AND ERROR TRAPPING<\/a>\n\t<\/li>\n<li>\n\t\t<a href=\"#seventh\">EXTENDED 2005 ERROR TRAPPING<\/a>\n\t<\/li>\n<\/ul>\n<p><\/code><\/p>\n<h2 id=\"first\">SQL Server 2000 &#8211; GENERATING AN ERROR <\/h2>\n<pre class=\"lang:tsql theme:ssms2012\">\r\nUSE pubs \r\nGO \r\nUPDATE dbo.authors \r\nSET zip = '!!!' \r\nWHERE au_id = '807-91-6654' \r\n\r\n\/* This will generate an error: \r\nMsg 547, Level 16, State 0, Line 1 \r\nThe UPDATE statement conflicted with the CHECK constraint  \r\n        \"CK__authors__zip__7F60ED59\". \r\nThe conflict occurred in database \"pubs\",  \r\n        table \"dbo.authors\", column 'zip'. \r\n        <\/pre>\n<h2>SQL Server 2005 &#8211; GENERATING AN ERROR <\/h2>\n<pre class=\"lang:tsql theme:ssms2012\">\r\nUSE AdventureWorks; \r\nGO \r\nUPDATE HumanResources.Employee \r\nSET MaritalStatus = 'H' \r\nWHERE EmployeeID = 100; \r\n\r\n\/* This generates a familiar error: \r\nMsg 547, Level 16, State 0, Line 1 \r\nThe UPDATE statement conflicted with the CHECK constraint  \r\n        \"CK_Employee_MaritalStatus\". \r\nThe conflict occurred in database \"AdventureWorks\", \r\n   table \"HumanResources.Employee\", column 'MaritalStatus'. \r\nThe statement has been terminated. \r\n<\/pre>\n<h2 id=\"second\">SQL Server 2000 AND 2005 &#8211; ERROR SEVERITY AND EXCEPTION TYPE<\/h2>\n<p>The error message provides several pieces of information: <\/p>\n<dl>\n<dt>Msg<\/dt>\n<dd>A message number identifies the type fo error. Can up to the  value of 50000. From that point forward custom user defined  error messages can be defined. <\/dd>\n<dt>Level  <\/dt>\n<dd>\n<p>The severity level of the error. <\/p>\n<ul>\n<li>10 and lower are informational.<\/li>\n<li>11-16 are errors in code or programming, like the error above.<\/li>\n<li>Errors 17-25 are resource or hardware errors.<\/li>\n<li>Any error with a severity of 20 or higher will terminate the connection (if not the server).<\/li>\n<\/ul>\n<\/dd>\n<dt>Line<\/dt>\n<dd>Defines which line number the error occurred on and can come  in extremely handy when troubleshooting large scripts or stored  procedures. <\/dd>\n<dt>Message Text   <\/dt>\n<dd>The informational message returned by SQL Server. <\/dd>\n<\/dl>\n<p>Error messages are defined and stored in the system table <code>sysmessages<\/code>.<\/p>\n<h2 id=\"third\">SQL Server 2000 &#8211; CATCH AN ERROR<\/h2>\n<p>SQL Server 2000 does not allow us to stop this error being returned, but we can try to deal with it in some fashion. The core method for  determining if a statement has an error in SQL Server 2000 is the  <code>@@ERROR<\/code> value. When a statement completes, this value is set.<\/p>\n<p>If the value equals <code>zero(0)<\/code>, no error occured. Any other value was the  result of an error.<\/p>\n<p>The following TSQL will result in the statement &#8216;A constraint error has occurred&#8217; being printed,as well as the error.<\/p>\n<pre class=\"lang:tsql theme:ssms2012\">\r\nUSE pubs \r\nGO \r\nUPDATE dbo.authors \r\nSET zip = '!!!' \r\nWHERE au_id = '807-91-6654' \r\nIF @@ERROR = 547 \r\n    PRINT 'A constraint error has occurred' \r\nGO \r\n<\/pre>\n<p><code>@@ERROR<\/code> is reset by each and every statement as it occurs. This means that if we use the exact same code as above, but check the <code>@@ERROR<\/code> function a second time, it will be different. <\/p>\n<pre class=\"lang:tsql theme:ssms2012\">\r\nUPDATE dbo.authors \r\nSET zip = '!!!' \r\nWHERE au_id = '807-91-6654' \r\nIF @@ERROR = 547 \r\n    PRINT 'A constraint error has occurred. Error Number:'  \r\n      + CAST(@@ERROR AS VARCHAR) \r\nGO \r\n<\/pre>\n<p>You will see the error number as returned by the <code>@@ERROR<\/code> statement as being zero(0), despite the fact that we just had a clearly  defined error.<\/p>\n<p>The problem is, while the <code>UPDATE<\/code> statement did in fact error out,  the <code>IF<\/code> statement executed flawlessly and <code>@@ERROR<\/code> is reset after each and every statement in SQL Server.<\/p>\n<p>In order to catch and keep these errors, you need to capture the  <code>@@ERROR<\/code> value after each execution.<\/p>\n<pre class=\"lang:tsql theme:ssms2012\">\r\nDECLARE @err INT \r\nUPDATE dbo.authors \r\nSET zip = '!!!' \r\nWHERE au_id = '807-91-6654' \r\nSET @err = @@ERROR \r\nIF @err = 547 \r\n    PRINT 'A constraint error has occurred. Error Number:'  \r\n      + CAST(@err AS VARCHAR) \r\nGO \r\n<\/pre>\n<p>Now we can capture the error number and refer to it as often as needed within the code.  <\/p>\n<h2> SQL Server 2005 &#8211; CATCH AN ERROR<\/h2>\n<p>While <code>@@ERROR<\/code> is still available in SQL Server 2005, a new syntax has  been added to the T-SQL language, as implemented by Microsoft: <code>TRY... CATCH<\/code>.<\/p>\n<p>This allows us to finally begin to perform real error trapping.<\/p>\n<pre class=\"lang:tsql theme:ssms2012\">\r\nBEGIN TRY  \r\n    UPDATE HumanResources.Employee \r\n    SET MaritalStatus = 'H' \r\n    WHERE EmployeeID = 100; \r\nEND TRY \r\nBEGIN CATCH \r\n    PRINT 'Error Handled'; \r\nEND CATCH \r\n<\/pre>\n<p>While there is an error encountered in the code, none is returned to the calling function. In fact, all that will happen in this case is the string <code>'Error Handled'<\/code> is returned to the client.<\/p>\n<p>We have actually performed the function of error trapping within TSQL.<\/p>\n<p>There are a number of issues around the use of <code>TRY...CATCH<\/code> that have  to be dealt with, which we will cover later. For example, simply having a <code>TRY...CATCH<\/code> statement is not enough.<\/p>\n<p>Consider this example:<\/p>\n<pre class=\"lang:tsql theme:ssms2012\">\r\n\r\n    UPDATE HumanResources.Employee \r\n    SET ContactID = 19978 \r\n    WHERE EmployeeID = 100; \r\nBEGIN TRY  \r\n    UPDATE HumanResources.Employee \r\n    SET MaritalStatus = 'H' \r\n    WHERE EmployeeID = 100; \r\nEND TRY \r\nBEGIN CATCH \r\n    PRINT 'Error Handled'; \r\nEND CATCH \r\n<\/pre>\n<p>The second error is handled, but the first one is not and we would see this error returned to client application: <\/p>\n<pre class=\"lang:tsql theme:ssms2012\">\r\nMsg 547, LEVEL 16, State 0, Line 1 \r\nThe UPDATE statement conflicted WITH the FOREIGN KEY CONSTRAINT  \r\n      \"FK_Employee_Contact_ContactID\" \r\nThe conflict occurred IN DATABASE \"AdventureWorks\"  \r\n      TABLE \"Person.Contact\" COLUMN 'ContactID'. \r\n      <\/pre>\n<p>To eliminate this problem place multiple statements within the <code>TRY<\/code> statement.<\/p>\n<h2 id=\"fourth\"> SQL Server 2000 &#8211; USING RAISERROR<\/h2>\n<p>The <code>RAISERROR<\/code> function is a mechanism for returning to calling  applications errors with your own message. It can use system error messages or custom error messages. The  basic syntax is easy: <\/p>\n<pre class=\"lang:tsql theme:ssms2012\">\r\nRAISERROR ('You made a HUGE mistake',10,1) \r\n<\/pre>\n<p>To execute <code>RAISERROR<\/code> you&#8217;ll either generate a string, up to 400 characters long, for the message, or you&#8217;ll access a message by  message id from the <code>master.dbo.sysmessages<\/code> table.<\/p>\n<p>You also choose the severity of the error raised. Severity levels used in <code>RAISERROR<\/code> will behave exactly as if the engine itself had  generated the error. This means that a <code>SEVERITY<\/code> of 20 or above will terminate the connection. The last number is an arbitrary value that has to be between 1 and 127.<\/p>\n<p>You can format the message to use variables. This makes it more  useful for communicating errors:<\/p>\n<pre class=\"lang:tsql theme:ssms2012\">\r\nRAISERROR('You broke the server: %s',10,1,@@SERVERNAME) \r\n<\/pre>\n<p>You can use a variety of different variables. You simply have to  declare them by data type and remember that, even with variables,  you have a 400 character limit. You also have some formatting options. <\/p>\n<pre class=\"lang:tsql theme:ssms2012\">\r\n--Unsigned Integer \r\nRAISERROR('The current error number: %u',10,1,@@ERROR) \r\n\r\n--String \r\nRAISERROR('The server is: %s',10,1,@@SERVERNAME) \r\n\r\n--Compound String &amp; Integer &amp; limit length of string to first 5  \r\n--characters \r\nRAISERROR('The server is: %.5s. The error is: %u',10,1, \r\n      @@SERVERNAME,@@ERROR) \r\n\r\n--String with a minimum and maximum length and formatting to left \r\nRAISERROR('The server is: %-7.3s',10,1,@@SERVERNAME) \r\n<\/pre>\n<p>A few notes about severity and status. Status can be any number up  to 127 and you can make use of it on your client apps. Setting the  Status to 127 will cause ISQL and OSQL to return the error number  to the operating environment. <\/p>\n<pre class=\"lang:tsql theme:ssms2012\">\r\n-- To get the error into the SQL Server Error Log \r\nRAISERROR('You encountered an error',18,1) WITH LOG \r\n\r\n-- To immediately return the error to the application \r\nRAISERROR('You encountered an error',10,1) WITH NOWAIT \r\n\r\n-- That also flushes the output buffer so any pending PRINT statements, \r\n-- etc., are cleared. \r\n\r\n-- To use RAISERROR as a debug statement \r\nRAISERROR('I made it to this part of the code',0,1) \r\n<\/pre>\n<h2>SQL SERVER 2005 &#8211; USING RAISERROR<\/h2>\n<p>The function of <code>RAISERROR<\/code> in SQL Server 2005 is largely the same as for  SQL 2000. However, instead of 400 characters, you have 2047. If you use  2048 or more, then 2044 are displayed along with an ellipsis.<\/p>\n<p><code>RAISERROR<\/code> will cause the code to jump from the <code>TRY<\/code> to the <code>CATCH<\/code> block.<\/p>\n<p>Because of the new error handling capabilities, <code>RAISERROR<\/code> can be called in a more efficient manner in SQL Server 2005. This from the Books  Online:<\/p>\n<pre class=\"lang:tsql theme:ssms2012\">\r\nBEGIN TRY  \r\n    RAISERROR('Major error in TRY block.',16,1); \r\nEND TRY \r\nBEGIN CATCH \r\n    DECLARE @ErrorMessage NVARCHAR(4000), \r\n        @ErrorSeverity INT, \r\n        @ErrorState INT; \r\n\r\n    SET @ErrorMessage = ERROR_MESSAGE(); \r\n    SET @ErrorSeverity = ERROR_SEVERITY(); \r\n    SET @ErrorState = ERROR_STATE(); \r\n\r\n    RAISERROR(@ErrorMessage,@ErrorSeverity,@ErrorState); \r\nEND CATCH; \r\n<\/pre>\n<h2 id=\"fifth\"> SQL Server 2000 &#8211; RETURNING ERROR CODES FROM STORED PROCEDURES<\/h2>\n<p>Stored procedures, by default, return the success of execution as  either zero or a number representing the failure of execution, but not necessarily the error number encountered. <\/p>\n<pre class=\"lang:tsql theme:ssms2012\">\r\nCREATE PROCEDURE dbo.GenError \r\nAS \r\nUPDATE dbo.authors \r\nSET zip = '!!!' \r\nWHERE au_id = '807-91-6654'  \r\nGO \r\n\r\nDECLARE @err INT \r\nEXEC @err = GenError \r\nSELECT @err \r\n<\/pre>\n<p>This will cause an error and the <code>SELECT<\/code> statement will return a  non-zero value. On my machine, -6. In order take control of this, modify the procedure as follows: <\/p>\n<pre class=\"lang:tsql theme:ssms2012\">\r\nALTER PROCEDURE dbo.GenError \r\nAS \r\nDECLARE @err INT \r\nUPDATE dbo.authors \r\nSET zip = '!!!' \r\nWHERE au_id = '807-91-6654' \r\nSET @err = @@ERROR \r\nIF @err &lt;&gt; 0 \r\n    RETURN @err \r\nELSE \r\n    RETURN 0 \r\nGO \r\n\r\nDECLARE @err INT \r\nEXEC @err = GenError \r\nSELECT @err \r\n<\/pre>\n<p>This time the <code>SELECT @err<\/code> statement will return the 547 error number in the results. With that, you can begin to create a more  appropriate error handling routine that will evolve into a coding  best practice within your organization. <\/p>\n<pre class=\"lang:tsql theme:ssms2012\">\r\nALTER PROCEDURE dbo.GenError \r\nAS \r\nDECLARE @err INT \r\nUPDATE dbo.authors \r\nSET zip = '!!!' \r\nWHERE au_id = '807-91-6654' \r\nSET @err = @@ERROR \r\nIF @err &lt;&gt; 0 \r\n    BEGIN \r\n        IF @err = 547 \r\n            RAISERROR('Check Constraint Error occurred',16,1) \r\n        ELSE \r\n            RAISERROR('An unspecified error has occurred.',10,1) \r\n        RETURN @err \r\n    END \r\nELSE \r\n    RETURN 0 \r\nGO \r\n<\/pre>\n<h2>SQL Server 2005 &#8211; RETURNING ERROR CODES FROM STORED PROCEDURES<\/h2>\n<p>In order to appropriately handle errors you to know what they are. You may also want to return the errors to the calling application.  A number of new functions have been created so that you can  appropriately deal with different errors, and log, report, anything you need, the errors that were generated. <\/p>\n<pre class=\"lang:tsql theme:ssms2012\">\r\nCREATE PROCEDURE GenErr \r\nAS \r\n    BEGIN TRY \r\n        UPDATE HumanResources.Employee \r\n        SET ContactID = 19978 \r\n        WHERE EmployeeID = 100; \r\n    END TRY \r\n    BEGIN CATCH \r\n        PRINT 'Error Number: ' + CAST(ERROR_NUMBER() AS VARCHAR); \r\n        PRINT 'Error Message: ' + ERROR_MESSAGE(); \r\n        PRINT 'Error Severity: ' + CAST(ERROR_SEVERITY() AS VARCHAR); \r\n        PRINT 'Error State: ' + CAST(ERROR_STATE() AS VARCHAR); \r\n        PRINT 'Error Line: ' + CAST(ERROR_LINE() AS VARCHAR); \r\n        PRINT 'Error Proc: ' + ERROR_PROCEDURE(); \r\n    END CATCH \r\nGO \r\n\r\nDECLARE @err INT; \r\nEXEC @err = GenErr; \r\nSELECT @err; \r\n<\/pre>\n<p>When you run the code above, you should receive this on the client, in the message, with a non-zero number in the result set: <\/p>\n<pre class=\"lang:tsql theme:ssms2012\">\r\n(0 row(s) affected) \r\nError Number: 547 \r\nError Message: The UPDATE statement conflicted WITH the  \r\n           FOREIGN KEY CONSTRAINT \r\n          \"FK_Employee_Contact_ContactID\"  \r\n           The conflict occurred IN DATABASE \r\n           \"AdventureWorks\" TABLE \"Person.Contact\"  \r\n           COLUMN 'ContactID'. \r\nError Severity: 16 \r\nError State: 0 \r\nError Line: 4 \r\nError Proc: GenErr \r\n<\/pre>\n<p>In other words, everything you need to actually deal with errors  as they occur.<\/p>\n<p>You&#8217;ll also notice that the procedure returned an error value  (non-zero) even though we didn&#8217;t specify a return code. You can still specify a return value as before if you don&#8217;t want to leave it up to the engine.<\/p>\n<h2 id=\"sixth\"> SQL Server 2000 &#8211; TRANSACTIONS AND ERROR TRAPPING<\/h2>\n<p>The one area of control we do have in SQL Server 2000 is around the   transaction. In SQL Server 2000 you can decide to rollback or not,   those are your only options. You need to make decision regarding  whether or not to use <code>XACT_ABORT<\/code>. Setting it to <code>ON<\/code> will cause an   entire transaction to terminate and rollback in the event of any   runtime error. if you set it to <code>OFF<\/code>, then in some cases you can   rollback the individual statement within the transaction as opposed   to the entire transaction.<\/p>\n<p>Modify the procedure to handle transactions:<\/p>\n<pre class=\"lang:tsql theme:ssms2012\">\r\nALTER PROCEDURE dbo.GenError \r\nAS \r\nDECLARE @err INT \r\n\r\nBEGIN TRANSACTION \r\n\r\n    UPDATE dbo.authors \r\n    SET zip = '90210' \r\n    WHERE au_id = '807-91-6654' \r\n    SET @err = @@ERROR \r\n    IF @err &lt;&gt; 0 \r\n        BEGIN \r\n            IF @err = 547 \r\n            PRINT 'A constraint error has occurred.' \r\n            ELSE \r\n                PRINT 'An unspecified error has occurred.' \r\n            ROLLBACK TRANSACTION \r\n            RETURN @err \r\n        END \r\n         \r\n    UPDATE dbo.authors \r\n    SET zip = '!!!' \r\n    WHERE au_id = '807-91-6654' \r\n    SET @err = @@ERROR \r\n    IF @err &lt;&gt; 0 \r\n        BEGIN \r\n            IF @err = 547 \r\n                PRINT 'A constraint error has occurred.' \r\n            ELSE \r\n                PRINT 'An unspecified error has occurred.' \r\n            ROLLBACK TRANSACTION \r\n            RETURN @err \r\n        END \r\n    ELSE \r\n        BEGIN \r\n            COMMIT TRANSACTION \r\n            RETURN 0 \r\n        END \r\nGO \r\n\r\nDECLARE @err INT \r\nEXEC @err = GenError \r\n\r\nSELECT zip  \r\nFROM dbo.authors \r\nWHERE au_id = '807-91-6654' \r\n<\/pre>\n<p>\nSince the above code will generate an error on the second statement, the transaction is rolled back as a unit. Switch to the results in  order to see that the zip code is, in fact, still 90210. If we wanted to control each update as a seperate statement, in order to get one  of them to complete, we could encapsulate each statement in a  transaction:\n<\/p>\n<pre class=\"lang:tsql theme:ssms2012\">\r\nALTER PROCEDURE dbo.GenError \r\nAS \r\nDECLARE @err INT \r\n\r\nBEGIN TRANSACTION \r\n\r\n    UPDATE dbo.authors \r\n    SET zip = '90210' \r\n    WHERE au_id = '807-91-6654' \r\n    SET @err = @@ERROR \r\n    IF @err &lt;&gt; 0 \r\n        BEGIN \r\n            IF @err = 547 \r\n            PRINT 'A constraint error has occurred.' \r\n            ELSE \r\n                PRINT 'An unspecified error has occurred.' \r\n            ROLLBACK TRANSACTION \r\n            RETURN @err \r\n        END \r\n    ELSE \r\n        COMMIT TRANSACTION \r\n\r\nBEGIN TRANSACTION     \r\n    UPDATE dbo.authors \r\n    SET zip = '!!!' \r\n    WHERE au_id = '807-91-6654' \r\n    SET @err = @@ERROR \r\n    IF @err &lt;&gt; 0 \r\n        BEGIN \r\n            IF @err = 547 \r\n                PRINT 'A constraint error has occurred.' \r\n            ELSE \r\n                PRINT 'An unspecified error has occurred.' \r\n            ROLLBACK TRANSACTION \r\n            RETURN @err \r\n        END \r\n    ELSE \r\n        BEGIN \r\n            COMMIT TRANSACTION \r\n            RETURN 0 \r\n        END \r\nGO \r\n\r\nDECLARE @err INT \r\nEXEC @err = GenError \r\n\r\nSELECT zip  \r\nFROM dbo.authors \r\nWHERE au_id = '807-91-6654' \r\n<\/pre>\n<p>In this case then, the return value will be &#8216;90210&#8217; since the first   update statement will complete successfully. Be sure that whatever   mechanism you use to call procedures does not itself begin a  transaction as part of the call or the error generated will result   in a rollback, regardless of the commit within the procedure. In the  next example, we&#8217;ll create a transaction that wraps the other two   transactions, much as a calling program would. If we then check for  errors and commit or rollback based on the general error state, it&#8217;s  as if the inner transaction that was successful never happened, as the  outer transaction rollback undoes all the work within it. <\/p>\n<pre class=\"lang:tsql theme:ssms2012\">\r\nALTER PROCEDURE dbo.GenError \r\nAS \r\nDECLARE @err1 INT \r\nDECLARE @err2 INT \r\n\r\nBEGIN TRANSACTION \r\n    BEGIN TRANSACTION \r\n\r\n        UPDATE dbo.authors \r\n        SET zip = '90211' \r\n        WHERE au_id = '807-91-6654' \r\n        SET @err1 = @@ERROR \r\n        IF @err1 &lt;&gt; 0 \r\n            BEGIN \r\n                IF @err1 = 547 \r\n                PRINT 'A constraint error has occurred.' \r\n                ELSE \r\n                    PRINT 'An unspecified error has occurred.' \r\n                ROLLBACK TRANSACTION \r\n                RETURN @err1 \r\n            END \r\n        ELSE \r\n            COMMIT TRANSACTION \r\n\r\n    BEGIN TRANSACTION     \r\n        UPDATE dbo.authors \r\n        SET zip = '!!!' \r\n        WHERE au_id = '807-91-6654' \r\n        SET @err2 = @@ERROR \r\n        IF @err2 &lt;&gt; 0 \r\n            BEGIN \r\n                IF @err2 = 547 \r\n                    PRINT 'A constraint error has occurred.' \r\n                ELSE \r\n                    PRINT 'An unspecified error has occurred.' \r\n                ROLLBACK TRANSACTION \r\n                RETURN @err2 \r\n            END \r\n        ELSE \r\n            BEGIN \r\n                COMMIT TRANSACTION \r\n                RETURN 0 \r\n            END \r\nIF (@err1 &lt;&gt; 0) OR (@err2 &lt;&gt; 0) \r\n    ROLLBACK TRANSACTION \r\nELSE \r\n    COMMIT TRANSACTION \r\nGO \r\n\r\nDECLARE @err INT \r\nEXEC @err = GenError \r\n\r\nSELECT zip  \r\nFROM dbo.authors \r\nWHERE au_id = '807-91-6654' \r\n<\/pre>\n<h2> SQL Server 2005 &#8211; TRANSACTIONS AND ERROR TRAPPING<\/h2>\n<p>The new error handling changes how transactions are dealt with. You can now check the transaction state using <code>XACT_STATE()<\/code> function. Transactions can be: <\/p>\n<ul>\n<li>Closed (equal to zero (0))<\/li>\n<li>Open but unable to commit (-1)<\/li>\n<li>Open and able to be committed (1)<\/li>\n<\/ul>\n<p>From there, you can make a decision as to whether or not a transaction is committed or rolled back. <code>XACT_ABORT<\/code> works the same way. <\/p>\n<pre class=\"lang:tsql theme:ssms2012\">\r\nALTER PROCEDURE GenErr \r\nAS \r\n    BEGIN TRY \r\n        BEGIN TRAN \r\n            UPDATE HumanResources.Employee \r\n            SET ContactID = 1\/0 \r\n            WHERE EmployeeID = 100; \r\n        COMMIT TRAN \r\n    END TRY \r\n    BEGIN CATCH \r\n        IF (XACT_STATE()) = -1 \r\n        BEGIN \r\n            ROLLBACK TRAN; \r\n            RETURN ERROR_NUMBER(); \r\n        END \r\n        ELSE IF (XACT_STATE()) = 1 \r\n        BEGIN \r\n      --it now depends on the type of error or possibly the line number  \r\n      --of the error \r\n            IF ERROR_NUMBER() = 8134 \r\n            BEGIN \r\n                ROLLBACK TRAN; \r\n                RETURN ERROR_NUMBER(); \r\n            END \r\n            ELSE \r\n            BEGIN \r\n                COMMIT TRAN;  \r\n                RETURN ERROR_NUMBER(); \r\n            END \r\n        END \r\n    END CATCH \r\nGO \r\n\r\nDECLARE @err INT; \r\nEXEC @err = GenErr; \r\nSELECT @err; \r\n<\/pre>\n<h2 id=\"seventh\"> SQL Server 2005 &#8211; EXTENDED 2005 ERROR TRAPPING<\/h2>\n<p>With the new <code>TRY...CATCH<\/code> construct, it&#8217;s finally possible to do things about errors, other than just return them. Take for example the dreaded  deadlock. Prior to SQL Server 2005, the best you could hope for was to  walk through the error messages stored in the log recorded by setting  <code>TRACEFLAG<\/code> values. Now, instead, you can set up a retry mechanism to  attempt the query more than once.  <\/p>\n<pre class=\"lang:tsql theme:ssms2012\">\r\nALTER PROCEDURE GenErr \r\nAS \r\nDECLARE @retry AS tinyint,  \r\n      @retrymax AS tinyint,  \r\n      @retrycount AS tinyint; \r\n\r\nSET @retrycount = 0; \r\nSET @retrymax = 2; \r\nSET @retry = 1; \r\n\r\nWHILE @retry = 1 AND @retrycount &lt;= @retrymax \r\nBEGIN \r\n    SET @retry = 0; \r\n    BEGIN TRY \r\n        UPDATE HumanResources.Employee \r\n        SET ContactID = ContactID \r\n        WHERE EmployeeID = 100; \r\n    END TRY \r\n    BEGIN CATCH \r\n        IF (ERROR_NUMBER() = 1205) \r\n        BEGIN \r\n            SET @retrycount = @retrycount + 1; \r\n            SET @retry = 1; \r\n        END \r\n    END CATCH \r\nEND \r\nGO \r\n\r\nDECLARE @err INT; \r\nEXEC @err = GenErr; \r\nSELECT @err;\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Grant Fritchey steps into the workbench arena, with an example-fuelled examination of catching and gracefully handling errors in SQL 2000 and 2005, including worked examples of the new <code>TRY..CATCH<\/code> capabilities.&hellip;<\/p>\n","protected":false},"author":221792,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143531],"tags":[4178,4669,4671,4672,4150,4151,4668,4252,5778,4460],"coauthors":[6785],"class_list":["post-228","post","type-post","status-publish","format-standard","hentry","category-t-sql-programming-sql-server","tag-bi","tag-exceptions","tag-raiseerror","tag-severity-levels","tag-sql","tag-sql-server","tag-sql-server-error-handling","tag-t-sql-programming","tag-try-catch","tag-workbench"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/228","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221792"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=228"}],"version-history":[{"count":9,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/228\/revisions"}],"predecessor-version":[{"id":66386,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/228\/revisions\/66386"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=228"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=228"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=228"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=228"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}