{"id":1548,"date":"2013-01-03T00:00:00","date_gmt":"2013-01-03T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/handling-errors-in-sql-server-2012\/"},"modified":"2021-08-24T13:40:01","modified_gmt":"2021-08-24T13:40:01","slug":"handling-errors-in-sql-server-2012","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/database-administration-sql-server\/handling-errors-in-sql-server-2012\/","title":{"rendered":"Handling Errors in SQL Server 2012"},"content":{"rendered":"<div class=\"article-content\">\n<p class=\"start\">Since the release of SQL Server 2005, you&#8217;ve been able to handle errors in your T-SQL code by including a <strong>TRY&#8230;CATCH<\/strong> block that controls the flow of your script should an error occur, similar to how procedural languages have traditionally handled errors. The <strong>TRY&#8230;CATCH<\/strong> block makes it easy to return or audit error-related data, as well as take other actions. And within the block-specifically, the <strong>CATCH<\/strong> portion-you&#8217;ve been able to include a <strong>RAISERROR<\/strong> statement in order to re-throw error-related data to the calling application. However, with the release of SQL Server 2012, you now have a replacement for <strong>RAISERROR<\/strong>, the <strong>THROW<\/strong> statement, which makes it easier than ever to capture the error-related data. <\/p>\n<p>In this article, we&#8217;ll look at the <strong>TRY&#8230;CATCH<\/strong> block used with both the <strong>RAISERROR<\/strong> and <strong>THROW<\/strong> statements. The examples are based on a table I created in the <strong>AdventureWorks2012<\/strong> sample database, on a local instance of SQL Server 2012. Listing 1 shows the T-SQL script I used to create the <strong>LastYearSales<\/strong> table. <\/p>\n<pre class=\"lang:tsql theme:ssms2012\">USE AdventureWorks2012;\nGO\n\nIF OBJECT_ID('LastYearSales', 'U') IS NOT NULL\nDROP TABLE LastYearSales;\nGO\n\nSELECT\n  BusinessEntityID AS SalesPersonID,\n  FirstName + ' ' + LastName AS FullName,\n  SalesLastYear\nINTO\n  LastYearSales\nFROM\n  Sales.vSalesPerson\nWHERE\n  SalesLastYear &gt; 0;\nGO<\/pre>\n<p class=\"caption\">Listing 1: Creating the LastYearSales table <\/p>\n<p>The script should be fairly straightforward. I use a <strong>SELECT&#8230;INTO<\/strong> statement to retrieve data from the <strong>Sales.vSalesPerson<\/strong> view and insert it into the newly created table. However, to demonstrate how to handle errors, we need to add one more element to our table: a check constraint that ensures the <strong>SalesLastYear<\/strong> value is never less than zero. Listing 2 shows the <strong>ALTERTABLE<\/strong> statement I used to add the constraint. <\/p>\n<pre class=\"lang:tsql theme:ssms2012\">ALTER TABLE LastYearSales\nADD CONSTRAINT ckSalesTotal CHECK (SalesLastYear &gt;= 0);\nGO<\/pre>\n<p class=\"caption\">Listing 2: Adding a check constraint to the LastYearSales table <\/p>\n<p>The constraint makes it easy to generate an error when updating the table. All I have to do is try to add a negative amount to the <strong>SalesLastYear<\/strong> column, an amount large enough to cause SQL Server to throw an error. Once we&#8217;ve created our table and added the check constraint, we have the environment we need for the examples in this article. You can just as easily come up with your own table and use in the examples. Just be sure you have a way of violating a constraint or you come up with another mechanism to generate an error. The goal is to create a script that handles any errors. <\/p>\n<h1>Working with the TRY&#8230;CATCH Block<\/h1>\n<p>Once we&#8217;ve set up our table, the next step is to create a stored procedure that demonstrates how to handle errors. The procedure, <strong>UpdateSales<\/strong>, modifies the value in the <strong>SalesLastYear<\/strong> column in the <strong>LastYearSales<\/strong> table for a specified salesperson. It works by adding or subtracting an amount from the current value in that column. Listing 3 shows the script I used to create the procedure. Notice that I include two input parameters-<strong>@SalesPersonID<\/strong> and <strong>@SalesAmt<\/strong>-which coincide with the table&#8217;s <strong>SalesPersonID<\/strong> and <strong>SalesLastYear<\/strong> columns. <\/p>\n<pre class=\"lang:tsql theme:ssms2012\">USE AdventureWorks2012;\nGO\n\nIF OBJECT_ID('UpdateSales', 'P') IS NOT NULL\nDROP PROCEDURE UpdateSales;\nGO\n\nCREATE PROCEDURE UpdateSales\n  @SalesPersonID INT,\n  @SalesAmt MONEY = 0\nAS\nBEGIN\n  BEGIN TRY\n    BEGIN TRANSACTION;\n      UPDATE LastYearSales\n      SET SalesLastYear = SalesLastYear + @SalesAmt\n      WHERE SalesPersonID = @SalesPersonID;\n    COMMIT TRANSACTION;\n  END TRY\n  BEGIN CATCH\n    IF @@TRANCOUNT &gt; 0\n    ROLLBACK TRANSACTION;\n\n    DECLARE @ErrorNumber INT = ERROR_NUMBER();\n    DECLARE @ErrorLine INT = ERROR_LINE();\n    DECLARE @ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE();\n    DECLARE @ErrorSeverity INT = ERROR_SEVERITY();\n    DECLARE @ErrorState INT = ERROR_STATE();\n\n    PRINT 'Actual error number: ' + CAST(@ErrorNumber AS VARCHAR(10));\n    PRINT 'Actual line number: ' + CAST(@ErrorLine AS VARCHAR(10));\n\n    RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState);\n  END CATCH\nEND;\nGO<\/pre>\n<p class=\"caption\">Listing 3: Creating a stored procedure that contains a Try&#8230;Catch block <\/p>\n<p>The main body of the procedure definition, enclosed in the <strong>BEGIN&#8230;END<\/strong> block, contains the <strong>TRY&#8230;CATCH<\/strong> block, which itself is divided into the <strong>TRY<\/strong> block and the <strong>CATCH<\/strong> block. The <strong>TRY<\/strong> block starts with <strong>BEGINTRY<\/strong> and ends with <strong>ENDTRY<\/strong> and encloses the T-SQL necessary to carry out the procedure&#8217;s actions. In this case, I include an <strong>UPDATE<\/strong> statement that adds the <strong>@SalesAmount<\/strong> value to the <strong>SalesLastYear<\/strong> column. The statement is enclosed in <strong>BEGINTRANSACTION<\/strong> and <strong>COMMITTRANSACTION<\/strong> statements to explicitly start and commit the transaction. Examples vary in terms of where they include the transaction-related statements. (Some don&#8217;t include the statements at all.) Just keep in mind that you want to commit or rollback your transactions at the appropriate times, depending on whether an error has been generated. <\/p>\n<p>If the <strong>UPDATE<\/strong> statement runs successfully, the <strong>SalesLastYear<\/strong> value is updated and the operation is completed, in which case, the code in the <strong>CATCH<\/strong> block is never executed. However, if the <strong>UPDATE<\/strong> statement fails and SQL Server generates an error, the transaction is terminated and the database engine jumps to the <strong>CATCH<\/strong> block. The <strong>CATCH<\/strong> block starts with <strong>BEGINCATCH<\/strong> and ends with <strong>ENDCATCH<\/strong> and encloses the statements necessary to handle the error. <\/p>\n<p>For the stored procedure in Listing 3, the first step I take in the <strong>CATCH<\/strong> block is to roll back the transaction if it is still running. I start by using the <strong>@@TRANCOUNT<\/strong> function to determine whether any transactions are still open. <strong>@@TRANCOUNT<\/strong> is a built-in SQL Server function that returns the number of running transactions in the current session. In this case, there should be only one (if an error occurs), so I roll back that transaction. <\/p>\n<p>Next, I declare a set of variables based on system functions that SQL Server makes available within the scope of the <strong>CATCH<\/strong> block. The functions return error-related information that you can reference in your T-SQL statements. Currently, SQL Server supports the following functions for this purpose: <\/p>\n<ul>\n<li><strong>ERROR_NUMBER():<\/strong> The number assigned to the error.  <\/li>\n<li><strong>ERROR_LINE():<\/strong> The line number inside the routine that caused the error.  <\/li>\n<li><strong>ERROR_MESSAGE():<\/strong> The error message text, which includes the values supplied for any substitutable parameters, such as times or object names.  <\/li>\n<li><strong>ERROR_SEVERITY():<\/strong> The error&#8217;s severity.  <\/li>\n<li><strong>ERROR_STATE():<\/strong> The error&#8217;s state number.  <\/li>\n<li><strong>ERROR_PROCEDURE():<\/strong> The name of the stored procedure or trigger that generated the error. <\/li>\n<\/ul>\n<p>For this example, I use all but the last function, though in a production environment, you might want to use that one as well. <\/p>\n<p>After I declare the variables, I include two <strong>PRINT<\/strong> statements that display the values of the <strong>@ErrorNumber<\/strong> and <strong>@ErrorLine<\/strong> variables (along with some explanatory text). The reason I do this is to demonstrate the difference between what the actual values are and what the <strong>RAISERROR<\/strong> statement returns, as you&#8217;ll see shortly. <\/p>\n<p>The <strong>RAISERROR<\/strong> statement comes after the <strong>PRINT<\/strong> statements. The statement returns error information to the calling application. Generally, when using <strong>RAISERROR<\/strong>, you should include an error message, error severity level, and error state. The rules that govern the <strong>RAISERROR<\/strong> arguments and the values they return are a bit complex and beyond the scope of this article, but for the purposes of this example, I simply pass in the <strong>@ErrorMessage<\/strong>, <strong>@ErrorSeverity<\/strong>, and <strong>@ErrorState<\/strong> variables as arguments. <\/p>\n<div class=\"note\">\n<p class=\"note\"><strong>NOTE:<\/strong> For more information about the RAISERROR statement, see the topic &#8220;<a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms178592.aspx\">RAISERROR (Transact-SQL)<\/a>&#8221; in SQL Server Books Online. <\/p>\n<\/div>\n<p>That&#8217;s basically all you need to do to create a stored procedure that contains a <strong>TRY&#8230;CATCH<\/strong> block. In a moment, we&#8217;ll try out our work. But first, let&#8217;s retrieve a row from the <strong>LastYearSales<\/strong> table to see what the current value is for salesperson 288. Listing 4 shows the <strong>SELECT<\/strong> statement I used to retrieve the data. <\/p>\n<pre class=\"lang:tsql theme:ssms2012\">SELECT FullName, SalesLastYear\nFROM LastYearSales\nWHERE SalesPersonID = 288<\/pre>\n<p class=\"caption\">Listing 4: Retrieving date from the LastYearSales table <\/p>\n<p>Not surprisingly, the statement returns the name and total sales for this salesperson, as shown in Listing 5. As you can see, Rachel Valdez shows over $1.3 million dollars in sales for last year. <\/p>\n<pre class=\"lang:tsql theme:ssms2012\">FullName\t\tSalesLastYear\nRachel Valdez\t\t1307949.7917<\/pre>\n<p class=\"caption\">Listing 5: Data retrieved from the LastYearSales table <\/p>\n<p>Now let&#8217;s try out the <strong>UpdateSales<\/strong> stored procedure. Just for fun, let&#8217;s add a couple million dollars to Rachel Valdez&#8217;s totals. Listing 6 shows how I use the <strong>EXEC<\/strong> statement to call the procedure and pass in the salesperson ID and the $2 million. <\/p>\n<pre class=\"lang:tsql theme:ssms2012\">EXEC UpdateSales 288, 2000000;<\/pre>\n<p class=\"caption\">Listing 6: Running the UpdateSales stored procedure <\/p>\n<p>The stored procedure should run with no problem because we&#8217;re not violating the check constraint. If we were to execute the <strong>SELECT<\/strong> statement again (the one in Listing 4), our results would look similar to those shown in Listing 7. Notice all the extra cash. <\/p>\n<pre class=\"lang:tsql theme:ssms2012\">FullName\t\tSalesLastYear\nRachel Valdez\t\t3307949.7917<\/pre>\n<p class=\"caption\">Listing 7: Viewing the updated sales amount in the LastYearSales table <\/p>\n<p>Now let&#8217;s look what happens if we subtract enough from her account to bring her totals to below zero. In listing 8, I run the procedure once again, but this time specify <strong>-4000000<\/strong> for the amount. <\/p>\n<pre class=\"lang:tsql theme:ssms2012\">EXEC UpdateSales 288, -4000000;<\/pre>\n<p class=\"caption\">Listing 8: Causing the UpdateSales stored procedure to throw an error <\/p>\n<p>As you&#8217;ll recall, after I created the <strong>LastYearSales<\/strong> table, I added a check constraint to ensure that the amount could not fall below zero. As a result, the stored procedure now generates an error, which is shown in Listing 9. <\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">&#160;(0 row(s) affected)\nActual error number: 547\nActual line number: 9\nMsg 50000, Level 16, State 0, Procedure UpdateSales, Line 27\nThe UPDATE statement conflicted with the CHECK constraint \"ckSalesTotal\". The conflict occurred in database \"AdventureWorks2012\", table \"dbo.LastYearSales\", column 'SalesLastYear'.<\/pre>\n<p class=\"caption\">Listing 9: The error message returned by the UpdateSales stored procedure <\/p>\n<p>As expected, the information we included in the <strong>CATCH<\/strong> block has been returned. But notice that the actual error number (<strong>547<\/strong>) is different from the <strong>RAISERROR<\/strong> message number (<strong>50000<\/strong>) and that the actual line number (<strong>9<\/strong>) is different from the <strong>RAISERROR<\/strong> line number (<strong>27<\/strong>). In theory, these values should coincide. But as I mentioned earlier, the rules that govern <strong>RAISERROR<\/strong> are a bit quirky. <\/p>\n<h1>Working with the THROW Statement<\/h1>\n<p>To simplify returning errors in a <strong>CATCH<\/strong> block, SQL Server 2012 introduced the <strong>THROW<\/strong> statement. With the <strong>THROW<\/strong> statement, you don&#8217;t have to specify any parameters and the results are more accurate. You simply include the statement as is in the <strong>CATCH<\/strong> block. <\/p>\n<div class=\"note\">\n<p class=\"note\"><strong>NOTE:<\/strong> You can use the <strong>THROW<\/strong> statement outside of the <strong>CATCH<\/strong> block, but you must include parameter values to do so. For more information about the <strong>THROW<\/strong> statement, see the topic &#8220;<a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ee677615.aspx\">THROW (Transact-SQL)<\/a>&#8221; in SQL Server Books Online. <\/p>\n<\/div>\n<p>To demonstrate the <strong>THROW<\/strong> statement, I defined an <strong>ALTER PROCEDURE<\/strong> statement that modifies the <strong>UpdateSales<\/strong> procedure, specifically the <strong>CATCH<\/strong> block, as shown in Listing 10. <\/p>\n<pre class=\"lang:tsql theme:ssms2012\">ALTER PROCEDURE UpdateSales\n  @SalesPersonID INT,\n  @SalesAmt MONEY = 0\nAS\nBEGIN\n  BEGIN TRY\n    BEGIN TRANSACTION;\n      UPDATE LastYearSales\n      SET SalesLastYear = SalesLastYear + @SalesAmt\n      WHERE SalesPersonID = @SalesPersonID;\n    COMMIT TRANSACTION;\n  END TRY\n  BEGIN CATCH\n    IF @@TRANCOUNT &gt; 0\n    ROLLBACK TRANSACTION;\n\n    DECLARE @ErrorNumber INT = ERROR_NUMBER();\n    DECLARE @ErrorLine INT = ERROR_LINE();\n\n    PRINT 'Actual error number: ' + CAST(@ErrorNumber AS VARCHAR(10));\n    PRINT 'Actual line number: ' + CAST(@ErrorLine AS VARCHAR(10));\n\n    THROW;\n  END CATCH\nEND;\nGO<\/pre>\n<p class=\"caption\">Listing 10: Altering the UpdateSales stored procedure <\/p>\n<p>Notice that I retain the <strong>@ErrorNumber<\/strong> and <strong>@ErrorLine<\/strong> variable declarations and their related <strong>PRINT<\/strong> statements. I do so only to demonstrate the <strong>THROW<\/strong> statement&#8217;s accuracy. In actually, I need only to roll back the transaction and specify the <strong>THROW<\/strong> statement, without any parameters. <\/p>\n<p>Now let&#8217;s execute the stored procedure again, once more trying to deduct $4 million from the sales amount, as shown in Listing 11. <\/p>\n<pre class=\"lang:tsql theme:ssms2012\">EXEC UpdateSales 288, -4000000;<\/pre>\n<p class=\"caption\">Listing 11: Causing the UpdateSales stored procedure to throw an error <\/p>\n<p>Once again, SQL Server returns an error. Only this time, the information is more accurate. As you can see in Listing 12, the message numbers and line numbers now match. No longer do we need to declare variables or call system functions to return error-related information to the calling application. <\/p>\n<pre class=\"listing\">&#160;(0 row(s) affected)\nActual error number: 547\nActual line number: 8\nMsg 547, Level 16, State 0, Procedure UpdateSales, Line 8\nThe UPDATE statement conflicted with the CHECK constraint \"ckSalesTotal\". The conflict occurred in database \"AdventureWorks2012\", table \"dbo.LastYearSales\", column 'SalesLastYear'.<\/pre>\n<p class=\"caption\">Listing 12: The error message returned by the UpdateSales stored procedure <\/p>\n<p>As you can see, SQL Server 2012 makes handling errors easier than ever. Even if you&#8217;ve been using the <strong>TRY&#8230;CATCH<\/strong> block for a while, the <strong>THROW<\/strong> statement should prove a big benefit over <strong>RAISERROR<\/strong>. And if you&#8217;re new to error handling in SQL Server, you&#8217;ll find that the <strong>TRY&#8230;CATCH<\/strong> block and the <strong>THROW<\/strong> statement together make the process a fairly painless one, one well worth the time and effort it takes to learn and implement them. <\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>The error handling of SQL Server has always been somewhat mysterious. Now at last, the THROW statement has been included in SQL Server 2012 that, combined with the TRY &#8230; CATCH block, makes error handling far easier. Robert Sheldon explains all.&hellip;<\/p>\n","protected":false},"author":221841,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143527],"tags":[4178,4170,5779,4150,4151,5534,5778],"coauthors":[],"class_list":["post-1548","post","type-post","status-publish","format-standard","hentry","category-database-administration-sql-server","tag-bi","tag-database-administration","tag-error-handling","tag-sql","tag-sql-server","tag-sql-server-2012","tag-try-catch"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1548","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\/221841"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=1548"}],"version-history":[{"count":3,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1548\/revisions"}],"predecessor-version":[{"id":40847,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1548\/revisions\/40847"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=1548"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=1548"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=1548"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=1548"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}