sql 结构化异常处理,RAISERROR,THROW,TRY块,CATCH块

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了sql 结构化异常处理,RAISERROR,THROW,TRY块,CATCH块相关的知识,希望对你有一定的参考价值。

-- STRUCTURED EXCEPTION HANDLING is where you create a structure to intercept and handle errors. This is often done with a TRY block that tries to do something, and then you have a CATCH block that catches the error and then handles the error in the case that something fails during the TRY block. Common CATCH block techniques are to put in code that logs what the error was, and/or tries to fix the error, and/or tries to re-THROW the error so that any application you're using can do it's own error handling (ie expose the error to SLQ Server so it can do it's normal error handling), or THROW a custom error message that says something like 'an error occurred but it's been taken care of and you're good'. 

/*
Common ERROR object properties and ERROR object functions:

- Number (ERROR_NUMBER) - unique number assigned to the error

- Message (ERROR_MESSAGE) - error message text

- Severity (ERROR_SEVERITY) - severity class (0-25). 16 means miscellaneous user error (severity level is always 16 with THROW). If it's 10 or below, the client won't raise error on the client side, meaning it's considered an informational message. If it's 11 or above, it's an error message. If it's 20 or higher, that's considered a fatal error and it'll automatically cut your connection (you can't go and handle those errors).

- Procedure Name (ERROR_PROCEDURE) - name of the procedure or trigger that raised the error

- Line Number (ERROR_LINE) - number of the line that raised the error in the batch, procedure, trigger, or function
*/

-------------------------
-- THROW vs. RAISERROR --
-------------------------

-- There are two ways to throw an error - RAISERROR and THROW. THROW is the successor to the RAISERROR statement and does not require defining errors in sys.messages table. THROW allows choices when handling errors, such as handling specific errors in the local CATCH block, or passing errors to another process. Use THROW with parameters to pass a user defined error, or without parameters to re-raise the original error (must be written with a CATCH block). THROW always breaks (aborts), while RAISERROR continues processing and can change your control of flow depending on how the statement is nested. Severity level is always 16 with THROW.


------------------------------------------------------------------------
-- Simple forced divide by zero TRY with catch showing ERROR objects: --
------------------------------------------------------------------------
BEGIN TRY
	SELECT 1/0;
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;
GO

/* Results:

-----------

(0 row(s) affected)

ErrorNumber ErrorSeverity ErrorState  ErrorProcedure                ErrorLine   ErrorMessage
----------- ------------- ----------- ------------------ ---------- ----------------------------------------------
8134        16            1           NULL                          3           Divide by zero error encountered.

(1 row(s) affected)


*/

--------------------------
-- TRY AND CATCH BLOCKS --
--------------------------

-- A TRY block executes first, and if an error occurs in the TRY block, control is passed to another group of statements that is enclosed in a CATCH block. Note that name resolution errors can occur before the TRY block is compiled, which would result in the CATCH block not firing.

------------------------------------------
-- Simple TRY and CATCH example. CATCH block only runs if there's an exception in the TRY block. --
------------------------------------------

-- begins the TRY block
BEGIN TRY
	UPDATE Production.Product
	-- statement that will intentially divide by zero and therefore create an error
	SET PRICE = Price / 0
END TRY
-- begins the CATCH block that will run if an error results from the TRY block
BEGIN CATCH
  -- prints the following text
	PRINT 'The following error occurred';
	--Function that prints the error message encountered
	PRINT ERROR_MESSAGE();
	-- THROWs an error message
	THROW 50001, 'An error occurred', 0;
END CATCH;

/*

Result from query above - error is THROWN to the user, but not exposed to the application:

(0 row(s) affected)
The following error occurred:
Divide by zero error encountered.

*/

------------------------------------------
-- Example that handles the error and then re-THROWs the error so it's exposed to SQL Server and SQL Server can then go through its usual error handling. --
------------------------------------------

-- Begins TRY block
BEGIN TRY
	UPDATE Production.Product2
	-- statement that will intentionally cause a divide by zero error
	SET StandardCost = ProductID / 0;
END TRY
-- Begins CATCH block which will execute if there's an error in TRY block
BEGIN CATCH
	-- Prints 'The following error occurred:'
	PRINT 'The following error occurred:';
	-- Prints the error message encountered so user can see it.
	PRINT ERROR_MESSAGE();
	-- THROW by itself will THROW the currently outstanding error (rethrowing the error so the client application can get it and do any of it's own error handling - the client application will only see the error produced by the THROW, it will not see or handle the error message produced by the CATCH block).
	THROW;
END CATCH;


/*

Result from query above:

(0 row(s) affected)
The following error occurred:
Divide by zero error encountered.
Msg 8134, Level 16, State 1, Line 6
Divide by zero error encountered.

DETAIL OF THE RESULTS ABOVE
(0 row(s) affected)
-- error message from the CATCH block using the ERROR_MESSAGE() function, seen and handled by the user but not the client application
The following error occurred:
Divide by zero error encountered.
-- error message from the THROW, seen and handled by the client application (SQL Server)
Msg 8134, Level 16, State 1, Line 6
Divide by zero error encountered.

*/

-------------------------------------------------------------
-- example of using THROW to create a custom error message --
-------------------------------------------------------------

BEGIN TRY
	UPDATE products1
	SET Product = 'New Product'
	WHERE ProductID = 'kjg';
END TRY
BEGIN CATCH
	THROW 50001, 'This is not a valid product name you nimwit', 1;
END CATCH;

/* Results:

(0 row(s) affected)
Msg 50001, Level 16, State 1, Line 96
This is not a valid product name you nimwit

*/

------------------------------------------
-- example of more complicated version that CATCHes error and writes it to the AdventureWorks dbo.ErrorLog table, using the pre-existing dbo.uspLogError stored procedure. --
------------------------------------------

-- begins TRY block
BEGIN TRY
	UPDATE Production.Product2
	-- statement that will intentionally cause a divide by zero error
	SET StandardCost = ProductID / 0;
END TRY
-- begins CATCH block which will execute if there's an error in TRY block
BEGIN CATCH
	-- delcares variables @ErrorLogId to hold ID, @ErrorMsg to hold message
	DECLARE @ErrorLogID AS INT, @ErrorMsg AS VARCHAR(250);
	-- pre-existing dbo.uspLogError stored procedure that logs the error into pre-existing table dbo.ErrorLog with @ErrorLogID as an OUTPUT parameter
	EXECUTE dbo.uspLogError @ErrorLogID OUTPUT;
	-- sets error message to this text, inserting @ErrorLogID so the user can see which ErrorLogID to look at in dbo.ErrorLog for more information
	SET @ErrorMsg = 'The update failed because of an error. View error #'
		+ CAST(@ErrorLogID AS VARCHAR)
		+ ' in the error log for details.';
	-- THROWs custom error showing user contents of @ErrorMsg variable
	THROW 50001, @ErrorMsg, 0;
END CATCH;

/*
The above batch will cause the following error message:

(0 row(s) affected)
Msg 50001, Level 16, State 0, Line 80
The update failed because of an error. View error #4 in the error log for details.

*/

-- How to see the error in dbo.ErrorLog
SELECT * from dbo.ErrorLog;

/*
Result from dbo.ErrorLog showing you all of the errors that have thusfar been logged. The error message said that the error was recorded as #4, so you'll see that in the row where ErrorLogID = 4.

ErrorLogID  ErrorTime               UserName              ErrorNumber ErrorSeverity ErrorState  ErrorProcedure  ErrorLine   ErrorMessage
----------- ----------------------- --------------------- ------------- ----------- --------------------------- ----------- ---------------------------------
1           2017-10-20 08:53:07.997 dbo                   8134        16            1           NULL            2           Divide by zero error encountered.
2           2017-10-20 08:53:34.540 dbo                   8134        16            1           NULL            2           Divide by zero error encountered.
3           2018-01-12 19:15:57.500 dbo                   8134        16            1           NULL            3           Divide by zero error encountered.
4           2018-01-12 19:17:39.590 dbo                   8134        16            1           NULL            3           Divide by zero error encountered.

(4 row(s) affected)


*/

以上是关于sql 结构化异常处理,RAISERROR,THROW,TRY块,CATCH块的主要内容,如果未能解决你的问题,请参考以下文章

Sql server抛出异常(RAISERROR)

SQL Server2012中的Throw语句尝试 RAISERROR和THROW比较

sql server数据库中raiserror函数的用法

如何处理 SQL 数据库中的 RAISERROR

SQL Server 2012 Throw关键字

sql MS SQL RAISERROR