本文出處: http://www.cnblogs.com/wy123/p/6743515.html T-SQL編程與應用程式一樣,都有異常處理機制,比如異常的捕獲與異常的拋出,本文簡單介紹異常捕獲與異常拋出在T-SQL編程中的實際使用 。 異常處理簡單說明 異常捕獲在應用程式編程中非常常見,提供了處 ...
本文出處: http://www.cnblogs.com/wy123/p/6743515.html
T-SQL編程與應用程式一樣,都有異常處理機制,比如異常的捕獲與異常的拋出,本文簡單介紹異常捕獲與異常拋出在T-SQL編程中的實際使用 。
異常處理簡單說明
異常捕獲在應用程式編程中非常常見,提供了處理程式運行時出現的任何意外或異常情況的方法
剛畢業的時候對於異常處理迷茫不解,尤其是catch中又throw,既然catch或者不catch,都會throw,為什麼要catch後再throw?catch中到底要做什麼處理?
後來接觸的多了開始慢慢理解了異常處理這個機制,在應用程式和T-SQL中應該是類似的
可以簡單地這樣理解:
對於UI層面, 異常捕獲,個人理解就是對於可能發生異常的代碼段進行捕獲處理,給予用戶友好的提示信息,
防止應用程式崩潰(或者拋給給用戶一個後臺代碼錯誤的頁面)的一種做法。
如果是底層方法(這個底層可以這麼理解A方法調用B方法,B方法又調用C方法,C方法就是底層方法),
異常處理可以是在捕獲之後繼續拋出給上層調用者,讓調用者知道它調用的方法發生了什麼問題。
對於發生了異常的代碼本身,要記錄下來異常的原因,以便於問題的排查。
比如C方法中發生了異常,要告訴調用他的B方法“我發生了異常,異常原因是***”, 這種的話C就要拋出異常,
同時C要記錄異常的信息(通過不同方式將上面的異常原因記錄下來),供後繼排查問題作參考。
以上是理論基礎,下麵以T-SQL中的異常處理為例,簡單介紹一下異常處理方式和要做的事情,T-SQL中的異常處理。
catch塊中處理異常信息
首先藉助下麵兩張表來做示例說明。一張產品信息表,有產品Id和價格信息
下麵將通過一個新增產品信息的存儲過程說明如何進行異常處理
如下是新增產品信息的存儲過程,存超過程根據參數,插入到Product表中,
在插入數據的過程中,進行了異常捕獲,在catch代碼中,有兩個操作,
第一步是將異常信息插入ExceptionLog,當然,這個異常信息的格式可以自己定義,第二步拋出異常(throw),就基於上面的理論
首先為什麼要記錄異常,這個很容易理解,A寫的存儲過程給B去調用,B調用的時候發生了異常,將異常信息記錄下來有利於A去排查異常的具體原因
然後拋出異常,目的是告訴B,執行存儲過程的時候產生了異常,你的操作並沒有成功執行。
比如下麵代碼,執行的時候發生了主鍵衝突異常,throw的作用就是告訴調用者,執行存儲過程的時候發生了異常,並將詳細的錯誤信息拋出
當然實際中常見的異常也比較多,比如死鎖,主外鍵衝突,執行超時,沒有操作許可權等等各種無法估計到的異常,包括記錄異常信息的格式,可以自由選擇。
此時觀察Catch中記錄的ExceptionLog信息,也記錄了下麵
記錄下來的異常信息目的是事後排查問題,與上面直接“拋出”的異常信息作用不同的是,
一個是在異常的發生的時候告訴調用者,拋出異常是標明當前執行的代碼發生了異常,ExceptionLog記錄異常信息是做排查分析異常原因使用
如果不拋出異常,比如如下的代碼,註釋掉throw語句,等於是在catch塊中單純地記錄下來異常之後“吃掉”異常,會出現什麼情況
比如在Product表中已經存在Id = 1的記錄的情況下,執行如下代碼是失敗,
但是客戶端並沒有任何提示,調用方並不知道發生了什麼,調用存儲過程的時候到底是失敗了還是成功了?沒有一個明確的答案。
這就是catch中吃掉異常的後果。
因此正常情況下,catch中記錄完異常之後,要“拋出”異常,當異常發生的時候,明確地告訴調用方發生了什麼問題。
使用throw顯式拋出異常
某些情況下需要主動拋出異常的方式來中斷邏輯的執行,什麼意思?
就是說當前的邏輯,只有在滿足一定的條件下才能執行,如果條件不滿足,就要明確告訴調用方,你的條件不滿足,當前邏輯無法正常執行。
舉個例子,還是剛纔插入產品信息的存儲過程,如下
當插入產品信息的時候,只有產品價格大於0才是有效的產品信息,否則無法插入,
此時就可以通過拋出異常的方式明確地告訴調用者(當然也有其他方式),你的參數不合法,使用throw拋出自定義異常,強制中斷代碼的執行。
上面的方式只是舉個示例說明,正常情況下,調用方傳遞過來的參數都是經過校驗的,不會發生太低級的錯誤。
當然,實際應用中應該比這個複雜的多,無法保證調用者都是從用戶圖形界面(UI)發起的,也就是說無法通過直接的預先處理來確保輸入信息的合法性
對於類似上面的存儲過程
首先無法保證調用方永遠傳遞過來的參數是合法有效的,其次連調用者也無法確保自己的生成的參數的邏輯永遠不會發生錯誤。
此時對於邏輯上要求非常嚴謹的程式來說,就需要做類似上面主動拋出異常了來中斷代碼的執行了。
當然上面問題的處理方式也不止這一種,只是說異常處理的應用場景和方式。
throw語句的使用
最後說一下throw語句,
throw是必raiserror更加方便和直觀的異常拋出方式,也是推薦的異常處理方式,具體差異網上一大把就不多說了
throw有兩種使用方式,拋出自定義異常和直接在catch塊中拋出異常。
拋出自定義異常的時候有三個必須參數,下麵會細說,catch塊中可以直接用throw不需要任何參數的方式拋出捕獲到的異常
throw語句的前一句需要一分號結尾,前一句又不能保證一定有分號,
所以可以直接把分號寫在throw的前面,比如文中的;throw 50000,'Price can not be less than 0',1 寫法
當拋出自定義錯誤的時候,throw語句有三個參數,參考如下
throw語句安全級別預設為16並且不會被修改,換句話說就是throw語句執行之後將拋出錯誤,打斷當前Session的批處理語句,throw後面的語句將不會執行
第一個參數是錯誤號,用戶自定義錯誤號要大於50000(50000 to 2147483647)
第二個參數是自定義錯誤信息內容,可以根據需要自定義
第三個參數是Error State,他的作用是可以標記異常發生的位置,
比如同樣是“參數不能小於0”的錯誤提示,整個存儲過程中有可能有兩個地方進行同樣的校驗
就可以在兩個地方使用Error State不同分別來拋出異常;
throw 50000,'Price can not be less than 0',1
throw 50000,'Price can not be less than 0',2,
因為Error State不同,就可以根據具體的Error State更加方便地知道是哪個地方發生了異常,參考
throw語句存儲自定義異常到messages系統表
可以將自定義的異常信息加入到sys.messages 系統表中,先加英文的,再加中文的。
然後使用的時候可以使用這個預定義的message,而不是每次寫一個字元串
EXEC sp_addmessage @msgnum = 50001, @severity = 16, @msgtext = N'%s can not be less than 0', @lang = 'us_english'; EXEC sp_addmessage @msgnum = 50001, @severity = 16, @msgtext = N'%s 不能小於0', @lang = '簡體中文';
如下,存儲過程中先通過FORMATMESSAGE來格式化異常信息,然後使用throw 50001, @msg, 1;的方式拋出異常
然後在調用存儲過程是的時候,如下是異常被觸發時候的拋出自定義異常的場景。
總結:本文簡單介紹了T-SQL編程中的異常處理方式,異常處理的時候可以通過捕獲異常信息並記錄下來,確保代碼的維護性,也為問題排查提供參考依據。
也可以在確保滿足業務邏輯的條件下,主動拋出自定義異常的方式終止代碼的執行,來確保業務邏輯的正確性。