在數據庫中有時候須要把多個步驟的指令看成一個總體來運行,這個總體要麼所有成功,要麼所有失敗,這就須要用到事務。數據庫
一、 事務的特色服務器
事務有若干條T-SQL指令組成,而且全部的指令昨晚一個總體提交給數據庫系統,執行時,這組指令要麼所有執行完成,要麼所有取消。所以,事務是一個不可分割的邏輯單元。數據結構
事務有4個屬性:原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)以及持久性(Durability),也稱做事務的ACID屬性。app
原子性:事務內的全部工做要麼所有完成,要麼所有不完成,不存在只有一部分完成的狀況。ide
一致性:事務內的而後操做都不能違反數據庫的而後約束或規則,事務完成時有內部數據結構都必須是正確的。函數
隔離性:事務直接是相互隔離的,若是有兩個事務對同一個數據庫進行操做,好比讀取表數據。任何一個事務看到的全部內容要麼是其餘事務完成以前的狀態,要麼是其餘事務完成以後的狀態。一個事務不可能遇到另外一個事務的中間狀態。fetch
持久性:事務完成以後,它對數據庫系統的影響是持久的,即便是系統錯誤,從新啓動系統後,該事務的結果依然存在。spa
二、 事務的模式日誌
a、 顯示事務code
顯示事務就是用戶使用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語句,將隱式事務模式打開,下一個語句會啓動一個新的事物,再下一個語句又將啓動一個新事務。
三、 事務處理
經常使用T-SQL事務語句:
a、 begin transaction語句
開始事務,而@@trancount全局變量用來記錄事務的數目值加1,能夠用@@error全局變量記錄執行過程當中的錯誤信息,若是沒有錯誤能夠直接提交事務,有錯誤能夠回滾。
b、 commit transaction語句
提交事務,表示一個隱式或顯示的事務的結束,對數據庫所作的修改正式生效。並將@@trancount的值減1;
c、 rollback transaction語句
回滾事務,執行rollback tran語句後,數據會回滾到begin tran的時候的狀態
四、 事務的示例
--開始事務
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
遊標能夠對一個select的結果集進行處理,或是不須要所有處理,就會返回一個對記錄集進行處理以後的結果。
一、遊標其實是一種能從多條數據記錄的結果集中每次提取一條記錄的機制。遊標能夠完成:
# 容許定位到結果集中的特定行
# 從結果集的當前位置檢索一行或多行數據
# 支持對結果集中當前位置的進行修改
因爲遊標是將記錄集進行一條條的操做,因此這樣給服務器增長負擔,通常在操做複雜的結果集的狀況下,才使用遊標。SQL Server 2005有三種遊標:T-SQL遊標、API遊標、客戶端遊標。
二、遊標的基本操做
遊標的基本操做有定義遊標、打開遊標、循環讀取遊標、關閉遊標、刪除遊標。
A、 定義遊標
declare cursor_name --遊標名稱
cursor [local | global] --全局、局部
[forward only | scroll] --遊標滾動方式
[read_only | scroll_locks | optimistic] --讀取方式
for select_statements --查詢語句
[for update | of column_name ...] --修改字段
參數:
forward only | scroll:前一個參數,遊標只能向後移動;後一個參數,遊標能夠隨意移動
read_only:只讀遊標
scroll_locks:遊標鎖定,遊標在讀取時,數據庫會將該記錄鎖定,以便遊標完成對記錄的操做
optimistic:該參數不會鎖定遊標;此時,若是記錄被讀入遊標後,對遊標進行更新或刪除不會超過
B、 打開遊標
open cursor_name;
遊標打開後,可使用全局變量@@cursor_rows顯示讀取記錄條數
C、 檢索遊標
fetch cursor_name;
檢索方式以下:
fetch first; 讀取第一行
fetch next; 讀取下一行
fetch prior; 讀取上一行
fetch last; 讀取最後一行
fetch absolute n; 讀取某一行
若是n爲正整數,則讀取第n條記錄
若是n爲負數,則倒數提取第n條記錄
若是n爲,則不讀取任何記錄
fetch pelative n
若是n爲正整數,則讀取上次讀取記錄以後第n條記錄
若是n爲負數,則讀取上次讀取記錄以前第n條記錄
若是n爲,則讀取上次讀取的記錄
D、 關閉遊標
close cursor_name;
E、 刪除遊標
deallocate cursor_name;
三、遊標操做示例
--建立一個遊標declare cursor_stu cursor scroll for select id, name, age from student;--打開遊標open cursor_stu;--存儲讀取的值declare @id int, @name nvarchar(20), @age varchar(20);--讀取第一條記錄fetch first from cursor_stu into @id, @name, @age;--循環讀取遊標記錄print '讀取的數據以下:';--全局變量while (@@fetch_status = 0)begin print '編號:' + convert(char(5), @id) + ', 名稱:' + @name + ', 類型:' + @age; --繼續讀取下一條記錄 fetch next from cursor_stu into @id, @name, @age;end--關閉遊標close area_cursor;--刪除遊標--deallocate area_cursor;