{"id":5964,"date":"2013-07-03T00:00:03","date_gmt":"2013-07-03T00:00:03","guid":{"rendered":"http:\/\/craftydba.com\/?p=5964"},"modified":"2016-04-21T23:49:55","modified_gmt":"2016-04-21T23:49:55","slug":"error-handling-part-3","status":"publish","type":"post","link":"https:\/\/craftydba.com\/?p=5964","title":{"rendered":"Error Handling &#8211; Part 3"},"content":{"rendered":"<p>I am going to continue my articles on controlling the execution flow of statements in a Transaction SQL (T-SQL) program.  <\/p>\n<p>Every program has to be deterministic on what step to execute next.  Otherwise, the computer would be clueless on what to do next.<\/p>\n<p>The only exception to this rule is error handling.  A runtime error may cause the program to branch to a error handler and exit the program.<\/p>\n<p>Today, I want to talk about creating, altering and deleting custom error messages.  Therefore, the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms178649.aspx\">sys.sp_addmessage<\/a>, <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms175094.aspx\">sys.sp_altermessage<\/a> and <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms174369(v=sql.110).aspx\">sys.sp_dropmessage<\/a> are key functions you should be familiar with for maintaining these messages.<\/p>\n<p><span style=\"color: #008000;font-size: 12px;\">Why define a custom error message?<\/span><\/p>\n<p>These messages are great for implementing the business logic with stored procedures in the database.  When invalid condition arises during a requested task, return the custom error message to the application.<\/p>\n<p><span style=\"color: #008000;font-size: 12px;\">How do we use the custom error message?<\/span><\/p>\n<p>I will be going over a code example that uses the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ee677615.aspx\">THROW<\/a> statement to raise an error.  This is the new school way of throwing an error.<\/p>\n<p>The code snippet below creates a custom error message for error number 65000.  This is in preparation for a new custom stored procedure in the Adventure Works database.<\/p>\n<pre class=\"lang:TSQL theme:familiar mark:1,2-3\" title=\"custom error message\">\r\n--\r\n-- Create custom error messages\r\n--\r\n\r\n-- Use master database\r\nUSE [master]\r\nGO\r\n\r\n-- Remove old message\r\nIF EXISTS (SELECT error FROM sys.sysmessages WHERE error = 65000)\r\n    EXEC sys.sp_dropmessage 65000\r\nGO\r\n\r\n-- Add new message (custom)\r\nEXEC sys.sp_addmessage \r\n     @msgnum   = 65000\r\n   , @severity = 16\r\n   , @msgtext  = \r\n     N'A business rule violation was detected in stored procedure [usp_Change_Pay_Rate] - %d.  %s'\r\n   , @lang = 'us_english'; \r\nGO\r\n\r\n<\/pre>\n<\/p>\n<p>The TSQL code below creates a stored procedure named [usp_Change_Pay_Rate1] which changes the pay rate for a given business entity.  There are two custom error calls using the THROW statement.  One for invalid business entity (1) and one for unusual percent change in rate (2).<\/p>\n<p>In mathematics, the <a href=\"http:\/\/www.mathgoodies.com\/lessons\/percent\/change.html\">percent change<\/a> is defined as how much the new value changes from the old value.  We want to generate an error when the increase\/decrease in pay rate exceeds 30 percent.<\/p>\n<p>The <a href=\"http:\/\/msdn.microsoft.com\/ru-ru\/library\/ms186788.aspx\">FORMATMESSAGE<\/a> function is used to take our two parameters (section of procedure and detailed message) and package them up for a call to the THROW statement.  The <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms174998.aspx\">RETURN<\/a> statement exits the stored procedure after the invalid condition is detected.<\/p>\n<pre class=\"lang:TSQL theme:familiar mark:1,2-3\" title=\"throw() function - error handling\">\r\n\r\n--\r\n-- Create custom stored procedure\r\n--\r\n\r\n-- Use sample database\r\nUSE [AdventureWorks2012]\r\nGO\r\n\r\n-- Drop existing procedure\r\nIF OBJECT_ID ('usp_Change_Pay_Rate1', 'P') IS NOT NULL \r\n    DROP PROCEDURE usp_Change_Pay_Rate1;\r\nGO\r\n\r\n-- Create new procedure\r\nCREATE PROCEDURE usp_Change_Pay_Rate1 @BusinessEntityID INT, @NewRate MONEY = 0\r\nAS\r\nBEGIN\r\n\r\n  -- Local variables\r\n  DECLARE @PctChange REAL = 0;\r\n  DECLARE @ErrorMsg NVARCHAR(2048) = '';\r\n\r\n  -- Get most recent record\r\n  SELECT TOP 1\r\n      BusinessEntityID,\r\n      RateChangeDate,\r\n      Rate,\r\n      PayFrequency,\r\n      ModifiedDate\r\n  INTO \r\n     #tmp_EmployeePayHistory\r\n  FROM \r\n      [AdventureWorks2012].[HumanResources].[EmployeePayHistory]\r\n  WHERE\r\n      BusinessEntityID = @BusinessEntityID \r\n  ORDER BY\r\n      [ModifiedDate] DESC;\r\n\r\n  -- Bad business entity id?\r\n  IF (@@ROWCOUNT <> 1)\r\n  BEGIN\r\n      SET @ErrorMsg = FORMATMESSAGE(65000, 1, N'Please make sure a valid business entity id was entered.'); \r\n      THROW 65000, @ErrorMsg, 1;    \r\n      RETURN;\r\n  END;\r\n  \r\n  -- Calculate percent change        \r\n  SELECT @PctChange =  ABS((@NewRate - [Rate]) \/ [Rate] * 100)\r\n  FROM #tmp_EmployeePayHistory;\r\n\r\n  -- Must be more than current\r\n  IF (@PctChange > 30)\r\n  BEGIN\r\n      SET @ErrorMsg = FORMATMESSAGE(65000, 2, N'Please make sure the pay rate change is less than 30%.'); \r\n      THROW 65000, @ErrorMsg, 1;    \r\n      RETURN;\r\n  END;\r\n\r\n  -- Update the temporary record\r\n  UPDATE \r\n    #tmp_EmployeePayHistory\r\n  SET \r\n    Rate = @NewRate, \r\n    RateChangeDate = GETDATE(), \r\n    ModifiedDate = GETDATE();\r\n\r\n  -- Insert into table\r\n  INSERT INTO [AdventureWorks2012].[HumanResources].[EmployeePayHistory]\r\n  SELECT * FROM #tmp_EmployeePayHistory;\r\n\r\nEND;\r\n<\/pre>\n<\/p>\n<p>Last but not least, we need two test cases that trigger the custom errors.<\/p>\n<pre class=\"lang:TSQL theme:familiar mark:1,2-3\" title=\"test custom s.p.\">\r\n--\r\n-- Test custom stored procedure\r\n--\r\n\r\n-- Bad Business Entity Id\r\nEXEC usp_Change_Pay_Rate1 -1, 50\r\n\r\n-- Bad rate change\r\nEXEC usp_Change_Pay_Rate1 1, 500\r\n\r\n<\/pre>\n<\/p>\n<p>The resulting output of the test calls is shown below.  <\/p>\n<pre class=\"lang:TSQL theme:epicgeeks\" title=\"output\">\r\nmessage window: \r\n\r\nMsg 65000, Level 16, State 1, Procedure usp_Change_Pay_Rate1, Line 31\r\nA business rule violation was detected in stored procedure [usp_Change_Pay_Rate] - 1.  \r\nPlease make sure a valid business entity id was entered.\r\n\r\nMsg 65000, Level 16, State 1, Procedure usp_Change_Pay_Rate1, Line 44\r\nA business rule violation was detected in stored procedure [usp_Change_Pay_Rate] - 2.  \r\nPlease make sure the pay rate change is less than 30.\r\n\r\n<\/pre>\n<\/p>\n<p>In short, raising custom error messages with the THROW statement inside stored procedures is a great way to add a business logic layer to the database.<\/p>\n<p>Next time I will be talking about how you can raise a user defined error using the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms178592.aspx\">RAISERROR<\/a> statement.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I am going to continue my articles on controlling the execution flow of statements in a Transaction SQL (T-SQL) program. Every program has to be deterministic on what step to execute next. Otherwise, the computer would be clueless on what to do next. The only exception to this rule is error handling. A runtime error may cause the program to branch to a error handler and exit the program. Today, I want to talk about creating, altering and deleting custom error messages. Therefore, the sys.sp_addmessage, sys.sp_altermessage and sys.sp_dropmessage are key&hellip;<\/p>\n","protected":false},"author":1,"featured_media":5878,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9],"tags":[883,31,671,888,15,324,28,884,885,886,887,29],"class_list":["post-5964","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-db-dev","tag-custom-error-messages","tag-database-developer","tag-error-handling","tag-formatmessage","tag-john-f-miner-iii","tag-return","tag-sql-server","tag-sys-sp_addmessage","tag-sys-sp_altermessage","tag-sys-sp_dropmessage","tag-throw","tag-tsql"],"_links":{"self":[{"href":"https:\/\/craftydba.com\/index.php?rest_route=\/wp\/v2\/posts\/5964","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/craftydba.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/craftydba.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/craftydba.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/craftydba.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=5964"}],"version-history":[{"count":0,"href":"https:\/\/craftydba.com\/index.php?rest_route=\/wp\/v2\/posts\/5964\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/craftydba.com\/index.php?rest_route=\/wp\/v2\/media\/5878"}],"wp:attachment":[{"href":"https:\/\/craftydba.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=5964"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/craftydba.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=5964"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/craftydba.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=5964"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}