對於所有的開發人員來說,Exception Handling是我們每天都要面對的事情。對於基於Source Code的Exception Handling,我想大家已經司空見慣了,但是對於Database級別的Exception Handling,就沒有那麼常見了。在這篇文章中,我將會介紹我對於基於Database編程中Exception Handling的一些粗淺的認識:在編寫Stored Procedure時,如何拋出一個可預知的Exception,ADO.NET如何處理從Database拋出的Exception,如何保存基於Database Exception的Error Message,如何在Database和.Net Application之間進行消息的傳遞[注:這裡的Database主要指SQL Server]。
@@ERROR
RAISEERROR
TRY CATCH & Return
Error message & sys.messages
ADO.Net Exception Handling
SqlException
InfoMessage
一、 @@ERROR
@@ERROR是我們經常使用的系統函數,其返回類型為INT,用以表示上一個語句的執行是否遇到錯誤,0便是語句正常執行,非0則以為著某個錯誤的產生。比如下面的一個例子:我在SQL Server Management Studio中執行下面一段簡單的SQL:
DECLARE @result INT
SET @result = 10/0
PRINT @@ERROR
Go
會得到如下的執行結果:
由於除零導致錯誤的產生,使@@ERROR的值變成8134。實際上8134是一個Error Number,代表某個系統定義的Error,每個預定義的Error由一個唯一的Error Number來唯一標識。@@ERROR本質上就是返回上一個語句執行遇到的Error的ID號,所以這並不是一個隨機的值,無論是10/0還是100/0,@@ERROR返回的結果都是一樣的。
由於可以通過@@ERROR得到Error的類型,可以幫助我們在編寫Stored Procedure的過程中,借助這個@@ERROR進行一些流程的控制。比如我們知道Error Number為547代表違反外鍵約束,我們就可以通過@@ERROR的結果是否等於547來進行異常的處理:
UPDATE T_USERS_IN_ROLES SET [USER_ID] = 'dummy text'
IF @@ERROR = 547
PRINT 'The user is not existent'
通過我們上面一個截圖我們發現,在
SQL Server Management Studio中執行任何一個語句的時候,如果遇到一個預定義的Error,會打印出相關的Error Message。這些Error Message到底是如何存儲的呢?Error message和Error Numbder又是如何進行關聯的呢?
實際上,SQL Server通過一個名為sys.messages的系統表來存儲關於Error的一些信息[關於sys.messages,在後續的章節中還會提及]。下面是sys.messages的結構:message_id不僅僅代表message的唯一標識,對於一個預定義的Error,其Error number就是這個message_id。由於Localization的需要,我們需要為不同的語言定義不同的Message,這些Message共享一個message_id, 具體采用何種語言通過luange_id來標識。Severity代表Error的嚴重程度,我將在後續部分專門介紹。is_event_logged是一個Indicator,表明出現該Error是否需要在Event log中進行日志記錄,text當然就是message文本了。
我們可以通過下面的SQL來進行驗證:
DECLARE @result INT
DECLARE @error INT
SET @result = 5/0
SET @error = @@ERROR
SELECT @error,sys.messages.* FROM sys.messages WHERE message_id = @error
Go
下面是執行的結果:
對於@@ERROR,有一點需要特別提醒的是:它僅僅代表前一個語句執行的Error Number,之後任何一段語句的執行都會改變@@ERROR的值,甚至是一個IF語句。我想通過下面一段SQL,你肯定會後一個深刻的認識:
我們可以看到,一個簡簡單單的IF語句就將@@ERROR從8134變成了0。不過想想也很簡單,IF語句本身也是一個執行語句,在執行過程中並沒有遇到Error,所以@@ERROR應該返回0。這也是我在上面的Sample中通過SET @error = @@ERROR將@@ERROR進行預存的原因。
@@ERROR實際上代表的是在編寫SQL或者Stored procedure中對異常的識別,大多數我們通過@@ERROR來判斷一段SQL語句是否成功執行,保證沒有遇到不可預知的異常。對於一些可以預知的異常在SQL中又該如何處理呢?
我發現很多程序員喜歡使用Output參數來處理這些預知的異常。比如:我們需要編寫一個添加User的Stored procedure,user name具有唯一性,添加一個和database中同名的user顯然是不合法的,在很多情況下通過一個Output參數來返回操作最終執行的情況,比如:
CREATE Procedure P_USERS_I
(
@user_id varchar(50),
@user_name nvarchar(256),
@flag INT OUTPUT
)
AS
IF(EXISTS(SELECT * FROM dbo.T_USERS WHERE LOWERED_USER_NAME = LOWER(@user_name) OR [USER_ID] = @user_id))
BEGIN
SET @flag = -1
RETURN
END
INSERT INTO dbo.T_USERS
([USER_ID]
,[USER_NAME]
,LOWERED_USER_NAME)
VALUES(@user_id, @user_name, LOWER(@user_name))
SET @flag = @@ERROR