轉自:
http://www.cnblogs.com/moss_tan_jun/archive/2011/11/26/2263988.html
-
事務
在數據庫中有時候需要把多個步驟的指令當作一個整體來運行,這個整體要么全部成功,要么全部失敗,這就需要用到事務。
1. 事務的特點
事務由若干條T-SQL指令組成,並且所有的指令作為一個整體提交給數據庫系統,執行時,這組指令要么全部執行完成,要么全部取消。因此,事務是一個不可分割的邏輯單元。
事務有4個屬性:原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)以及持久性(Durability),也稱作事務的ACID屬性。
原子性:事務內的所有工作要么全部完成,要么全部不完成,不存在只有一部分完成的情況。
一致性:事務內的然后操作都不能違反數據庫的然后約束或規則,事務完成時有內部數據結構都必須是正確的。
隔離性:事務直接是相互隔離的,如果有兩個事務對同一個數據庫進行操作,比如讀取表數據。任何一個事務看到的所有內容要么是其他事務完成之前的狀態,要么是其他事務完成之后的狀態。一個事務不可能遇到另一個事務的中間狀態。
持久性:事務完成之后,它對數據庫系統的影響是持久的,即使是系統錯誤,重新啟動系統后,該事務的結果依然存在。
2. 事務的模式
a. 顯示事務
顯示事務就是用戶使用T-SQL明確的定義事務的開始(begin transaction)和提交(commit transaction)或回滾事務(rollback transaction)
b. 自動提交事務
自動提交事務是一種能夠自動執行並能自動回滾事務,這種方式是T-SQL的默認事務方式。例如在刪除一個表記錄的時候,如果這條記錄有主外鍵關系的時候,刪除就會受主外鍵約束的影響,那么這個刪除就會取消。
可以設置事務進入隱式方式:set implicit_transaction on;
c. 隱式事務
隱式事務是指當事務提交或回滾后,SQL Server自動開始事務。因此,隱式事務不需要使用begin transaction顯示開始,只需直接失業提交事務或回滾事務的T-SQL語句即可。
使用時,需要設置set implicit_transaction on語句,將隱式事務模式打開,下一個語句會啟動一個新的事物,再下一個語句又將啟動一個新事務。
3. 事務處理
常用T-SQL事務語句:
a. begin transaction語句
開始事務,而@@trancount全局變量用來記錄事務的數目值加1,可以用@@error全局變量記錄執行過程中的錯誤信息,如果沒有錯誤可以直接提交事務,有錯誤可以回滾。
b. commit transaction語句
回滾事務,表示一個隱式或顯示的事務的結束,對數據庫所做的修改正式生效。並將@@trancount的值減1;
c. rollback transaction語句
回滾事務,執行rollback tran語句后,數據會回滾到begin tran的時候的狀態
4. 事務的示例
--開始事務 begin transaction tran_bank; declare @tran_error int; set @tran_error = 0; begin try update bank set totalMoney = totalMoney - 10000 where userName = 'jack'; set @tran_error = @tran_error + @@error; update bank set totalMoney = totalMoney + 10000 where userName = 'jason'; set @tran_error = @tran_error + @@error; end try begin catch print '出現異常,錯誤編號:' + convert(varchar, error_number()) + ', 錯誤消息:' + error_message(); set @tran_error = @tran_error + 1; end catch if (@tran_error > 0) begin --執行出錯,回滾事務 rollback tran; print '轉賬失敗,取消交易'; end else begin --沒有異常,提交事務 commit tran; print '轉賬成功'; end go
-
異常
在程序中,有時候完成一些Transact-SQL會出現錯誤、異常信息。如果我們想自己處理這些異常信息的話,需要手動捕捉這些信息。那么我們可以利用try catch完成。
TRY…CATCH 構造包括兩部分:一個 TRY 塊和一個 CATCH 塊。如果在 TRY 塊中所包含的 Transact-SQL 語句中檢測到錯誤條件,控制將被傳遞到 CATCH 塊(可在此塊中處理該錯誤)。
CATCH 塊處理該異常錯誤后,控制將被傳遞到 END CATCH 語句后面的第一個 Transact-SQL 語句。如果 END CATCH 語句是存儲過程或觸發器中的最后一條語句,控制將返回到調用該存儲過程或觸發器的代碼。將不執行 TRY 塊中生成錯誤的語句后面的 Transact-SQL 語句。
如果 TRY 塊中沒有錯誤,控制將傳遞到關聯的 END CATCH 語句后緊跟的語句。如果 END CATCH 語句是存儲過程或觸發器中的最后一條語句,控制將傳遞到調用該存儲過程或觸發器的語句。
TRY 塊以 BEGIN TRY 語句開頭,以 END TRY 語句結尾。在 BEGIN TRY 和 END TRY 語句之間可以指定一個或多個 Transact-SQL 語句。CATCH 塊必須緊跟 TRY 塊。CATCH 塊以 BEGIN CATCH 語句開頭,以 END CATCH 語句結尾。在 Transact-SQL 中,每個 TRY 塊僅與一個 CATCH 塊相關聯。
錯誤函數
TRY...CATCH 使用錯誤函數來捕獲錯誤信息:
ERROR_NUMBER() 返回錯誤號。
ERROR_MESSAGE() 返回錯誤消息的完整文本。此文本包括為任何可替換參數(如長度、對象名稱或時間)提供的值。
ERROR_SEVERITY() 返回錯誤嚴重性。
ERROR_STATE() 返回錯誤狀態號。
ERROR_LINE() 返回導致錯誤的例程中的行號。
ERROR_PROCEDURE() 返回出現錯誤的存儲過程或觸發器的名稱。
示例
錯誤消息存儲過程
if (object_id('proc_error_info') is not null) drop procedure proc_error_info go create proc proc_error_info as select error_number() '錯誤編號', error_message() '錯誤消息', error_severity() '嚴重性', error_state() '狀態好', error_line() '錯誤行號', error_procedure() '錯誤對象(存儲過程或觸發器)名稱'; go
示例:用異常處理錯誤信息
簡單try catch示例
begin try select 1 / 0; end try begin catch exec proc_error_info; --調用錯誤消息存儲過程 end catch go
示例:異常能處理的錯誤信息
簡單try catch示例,無法處理錯誤
begin try select * * from student; end try begin catch exec proc_error_info; end catch go
簡單try catch示例,不處理錯誤(不存在的表對象)
begin try select * from st; end try begin catch exec proc_error_info; end catch go
異常處理,能處理存儲過程(觸發器)中(不存在表對象)的錯誤信息
if (object_id('proc_select') is not null) drop procedure proc_select go create proc proc_select as select * from st; go begin try exec proc_select; end try begin catch exec proc_error_info; end catch go
示例:無法提交的事務
--創建臨時用表 if (object_id('temp_tab', 'u') is not null) drop table temp_tab go create table temp_tab( id int primary key identity(100000, 1), name varchar(200) ) go begin try begin tran; --沒有createTime字段 alter table temp_tab drop column createTime; commit tran; end try begin catch exec proc_error_info;--顯示異常信息 if (xact_state() = -1) begin print '會話具有活動事務,但出現了致使事務被歸類為無法提交的事務的錯誤。' + '會話無法提交事務或回滾到保存點;它只能請求完全回滾事務。' + '會話在回滾事務之前無法執行任何寫操作。會話在回滾事務之前只能執行讀操作。' + '事務回滾之后,會話便可執行讀寫操作並可開始新的事務。'; end else if (xact_state() = 0) begin print '會話沒有活動事務。'; end else if (xact_state() = 1) begin print '會話具有活動事務。會話可以執行任何操作,包括寫入數據和提交事務。'; end end catch go
示例:處理異常日志信息
--異常、錯誤信息表 if (object_id('errorLog', 'U') is not null) drop table errorLog go create table errorLog( errorLogID int primary key identity(100, 1), --ErrorLog 行的主鍵。 errorTime datetime default getDate(), --發生錯誤的日期和時間。 userName sysname default current_user, --執行發生錯誤的批處理的用戶。 errorNumber int, --發生的錯誤的錯誤號。 errorSeverity int, --發生的錯誤的嚴重性。 errorState int, --發生的錯誤的狀態號。 errorProcedure nvarchar(126), --發生錯誤的存儲過程或觸發器的名稱。 errorLine int, --發生錯誤的行號。 errorMessage nvarchar(4000) ) go --存儲過程:添加異常日志信息 if (object_id('proc_add_exception_log', 'p') is not null) drop proc proc_add_exception_log go create proc proc_add_exception_log(@logId int = 0 output) as begin set nocount on; set @logId = 0; begin try if (error_number() is null) return; if (xact_state() = -1) begin print '會話具有活動事務,但出現了致使事務被歸類為無法提交的事務的錯誤。' + '會話無法提交事務或回滾到保存點;它只能請求完全回滾事務。' + '會話在回滾事務之前無法執行任何寫操作。會話在回滾事務之前只能執行讀操作。' + '事務回滾之后,會話便可執行讀寫操作並可開始新的事務。'; end else if (xact_state() = 0) begin print '會話沒有活動事務。'; end else if (xact_state() = 1) begin print '會話具有活動事務。會話可以執行任何操作,包括寫入數據和提交事務。'; end --添加日志信息 insert into errorLog values(getDate(), current_user, error_number(), error_severity(), error_state(), error_procedure(), error_line(), error_message()); --設置自增值 select @logId = @@identity; end try begin catch print '添加異常日志信息出現錯誤'; exec proc_error_info;--顯示錯誤信息 return -1; end catch end go --處理異常信息示例 declare @id int; begin try begin tran; --刪除帶有外鍵的記錄信息 delete classes where id = 1; commit tran; end try begin catch exec proc_error_info;--顯示錯誤信息 if (xact_state() <> 0) begin rollback tran; end exec proc_add_exception_log @id output end catch select * from errorLog where errorLogID = @id; go