SQLServer2008に次のSQLステートメントがあるとします。
BEGIN TRANSACTION
SqlStatement1
EXEC sp1
SqlStatement3
COMMIT TRANSACTION
sp1のコード
BEGIN TRANSACTION
SqlStatement2
ROLLBACK TRANSACTION
私の質問は:SqlStatement3
実際に実行されますか?
SQLServer2008に次のSQLステートメントがあるとします。
BEGIN TRANSACTION
SqlStatement1
EXEC sp1
SqlStatement3
COMMIT TRANSACTION
sp1のコード
BEGIN TRANSACTION
SqlStatement2
ROLLBACK TRANSACTION
私の質問は:SqlStatement3
実際に実行されますか?
SQL Serverは、ネストされたトランザクションを実際にはサポートしていません。一度に1つのトランザクションのみがあります。
この1つのトランザクションには、基本的なネストされたトランザクションカウンターがあり@@TRANCOUNT
ます。連続するたびbegin transaction
に、カウンターが1ずつ増加し、それぞれが1ずつcommit transaction
減少します。カウンターを0に減らすものだけが、commit
実際に1つのトランザクションをコミットします。
Arollback transaction
は、1つのトランザクションを元に戻し、クリアします@@TRANCOUNT
。
あなたの場合、面白い結果はSqlStatement3がトランザクションの外で実行されるということです!ファイナルcommit
は「COMMITTRANSACTIONリクエストには対応するBEGINTRANSACTIONがありません」という例外をスローしますが、SqlStatement3の効果は永続的です。
例えば:
create table #t (col1 int)
insert #t (col1) values (1)
BEGIN TRANSACTION
update #t set col1 = 2 -- This gets rolled back
BEGIN TRANSACTION
update #t set col1 = 3 -- This gets rolled back too
ROLLBACK TRANSACTION
update #t set col1 = 4 -- This is run OUTSIDE a transaction!
COMMIT TRANSACTION -- Throws error
select col1 from #t
プリントし4
ます。本当に。:)
トランザクションセーブポイントを使用できます。sp1は、エラー処理とネストされたトランザクションで説明されているようなパターンを使用できます。
create procedure [usp_my_procedure_name]
as
begin
set nocount on;
declare @trancount int;
set @trancount = @@trancount;
begin try
if @trancount = 0
begin transaction
else
save transaction usp_my_procedure_name;
-- Do the actual work here
lbexit:
if @trancount = 0
commit;
end try
begin catch
declare @error int, @message varchar(4000), @xstate int;
select @error = ERROR_NUMBER(), @message = ERROR_MESSAGE(), @xstate = XACT_STATE();
if @xstate = -1
rollback;
if @xstate = 1 and @trancount = 0
rollback
if @xstate = 1 and @trancount > 0
rollback transaction usp_my_procedure_name;
raiserror ('usp_my_procedure_name: %d: %s', 16, 1, @error, @message) ;
end catch
end
このようなパターンにより、sp1で実行された作業をロールバックできますが、包含トランザクションはアクティブのままになります。
Rollback transaction
単独ですべてのトランザクションをロールバックします。
http://msdn.microsoft.com/en-us/library/ms181299(v=sql.100).aspx
ステートメントは引き続き実行されます-これを試してください
create table #t (i int)
insert #t values (1) -- t contains (1)
begin tran
update #t set i = i +1
select * from #t -- t contains (2)
begin tran
update #t set i = i +1
select * from #t -- t contains (3)
rollback tran -- transaction is rolled back
select * from #t -- t contains (1)
update #t set i = i +1
select * from #t -- t contains (2)
commit -- error occurs
select * from #t -- t contains (2)
drop table #t
ネストされたトランザクションを使用できます。内部トランザクションのみをロールバックするには、セーブポイントを使用してセーブポイントにロールバックします。内部トランザクションがネストされているかどうかに関係なく、IF
ステートメントを使用して、セーブポイントを設定するかどうか、およびセーブポイントにロールバックするかロールバックするかを確認できます。
BEGIN TRAN
DECLARE @WILL_BE_NESTED_TRANSACTION BIT = CASE WHEN (@@TRANCOUNT > 0) THEN 1 ELSE 0 END
IF @WILL_BE_NESTED_TRANSACTION = 1
SAVE TRAN tran_save
BEGIN TRAN
-- do stuff
IF @WILL_BE_NESTED_TRANSACTION = 1
ROLLBACK TRAN tran_save
ELSE
ROLLBACK
ROLLBACK
内部トランザクションのコミットは、SQLServerデータベースエンジンによって無視されます。トランザクションは、最も外側のトランザクションの最後に実行されたアクションに基づいて、コミットまたはロールバックされます。外部トランザクションがコミットされると、内部のネストされたトランザクションもコミットされます。外部トランザクションがロールバックされると、内部トランザクションが個別にコミットされたかどうかに関係なく、すべての内部トランザクションもロールバックされます。