ROLLBACKできるトランザクション内で行われた特定の変更のみをコミットする
-
03-07-2019 - |
質問
これは元の質問からの重要な編集であり、より簡潔にし、既存の回答によって提起されたポイントをカバーしています...
単一のトランザクション内で複数のテーブルに複数の変更を加え、一部の変更のみをロールバックすることは可能ですか?
以下のTSQLでは、" myLogSP"による変更は一切必要ありません。ロールバックされるまで。ただし、さまざまなmyBusinessSPによって行われたすべての変更は、必要に応じてロールバックする必要があります。
BEGIN TRANSACTION
EXEC myLogSP
EXEC @err = myBusinessSPa
IF (@err <> 0) BEGIN ROLLBACK TRANSACTION RETURN -1 END
EXEC myLogSP
EXEC @err = myBusinessSPb
IF (@err <> 0) BEGIN ROLLBACK TRANSACTION RETURN -1 END
EXEC myLogSP
EXEC @err = myBusinessSPc
IF (@err <> 0) BEGIN ROLLBACK TRANSACTION RETURN -1 END
EXEC myLogSP
COMMIT TRANSACTION
RETURN 0
順序は重要です。myLogSPはmyBusinessSPの前後に発生する必要があります(myLogSPはmyBusinessSPによって行われた変更を取得します)
また、データベースの整合性を維持し、必要に応じてすべての変更をロールバックできるように、すべてのmyBusinessSPが1つのトランザクション内で発生することも重要です。
myLogSPをトランザクションの一部ではないかのように動作させたいかのようです。 (myBusinessSPの間で呼び出される必要があるため)たまたま内部にあるのは不便な事実です。
編集:
最終的な答えは「いいえ」です。唯一のオプションはコードを再設計することです。ロギングにテーブル変数を使用するか(変数がロールバックされないため)、またはトランザクションを必要としないようにビジネスロジックを再設計します...
解決
SAVEPOINT
s を使用し、例
BEGIN TRANSACTION
EXEC myLogSP
SAVE TRANSACTION savepointA
EXEC @err = myBusinessSPa
IF (@err <> 0) BEGIN
ROLLBACK TRANSACTION savepointA
COMMIT
RETURN -1
END
EXEC myLogSP
SAVE TRANSACTION savepointB
EXEC @err = myBusinessSPb
IF (@err <> 0) BEGIN
ROLLBACK TRANSACTION savepointB
COMMIT
RETURN -1
END
EXEC myLogSP
SAVE TRANSACTION savepointC
EXEC @err = myBusinessSPc
IF (@err <> 0) BEGIN
ROLLBACK TRANSACTION savepointC
COMMIT
RETURN -1
END
EXEC myLogSP
COMMIT TRANSACTION
編集
これまでに提供された情報(および私の理解)に基づいて、変数を使用するか、ファイルを使用するか、またはそれらを実行できるようにするには、SPのロギングを再設計する必要があるようです次のとおりです。
BEGIN TRANSACTION
SAVE TRANSACTION savepointA
EXEC @err = myBusinessSPa
IF (@err <> 0) BEGIN
ROLLBACK TRANSACTION savepointA
EXEC myLogSPA -- the call to myBusinessSPa was attempted/failed
COMMIT
RETURN -1
END
SAVE TRANSACTION savepointB
EXEC @err = myBusinessSPb
IF (@err <> 0) BEGIN
ROLLBACK TRANSACTION savepointB
EXEC myLogSPA -- the call to myBusinessSPa originally succeeded
EXEC myLogSPB -- the call to myBusinessSPb was attempted/failed
COMMIT
RETURN -1
END
SAVE TRANSACTION savepointC
EXEC @err = myBusinessSPc
IF (@err <> 0) BEGIN
ROLLBACK TRANSACTION savepointC
EXEC myLogSPA -- the call to myBusinessSPa originally succeeded
EXEC myLogSPB -- the call to myBusinessSPb originally succeeded
EXEC myLogSPC -- the call to myBusinessSPc was attempted/failed
COMMIT
RETURN -1
END
EXEC myLogSPA -- the call to myBusinessSPa succeeded
EXEC myLogSPB -- the call to myBusinessSPb succeeded
EXEC myLogSPC -- the call to myBusinessSPc succeeded
COMMIT TRANSACTION
他のヒント
基本的に現在のコンテキストの外にジャンプする必要があります。それにはいくつかの方法があります。 1つ(試したことがない)は、CLRを呼び出して挿入を行うことです。
おそらくより良い方法は、テーブル変数がトランザクションの影響を受けないという事実を使用することです。例:
CREATE TABLE dbo.Test_Transactions
(
my_string VARCHAR(20) NOT NULL
)
GO
DECLARE
@tbl TABLE (my_string VARCHAR(20) NOT NULL)
BEGIN TRANSACTION
INSERT INTO dbo.Test_Transactions (my_string) VALUES ('test point one')
INSERT INTO @tbl (my_string) VALUES ('test point two')
INSERT INTO dbo.Test_Transactions (my_string) VALUES ('test point three')
ROLLBACK TRANSACTION
INSERT INTO dbo.Test_Transactions (my_string) select my_string from @tbl
SELECT * FROM dbo.Test_Transactions
SELECT * FROM @tbl
GO
ログエントリをテーブル変数に格納し、コミットまたはロールバック後に実際のテーブルに挿入することができました。
[OK]をクリックして、SQL Server 2008を使用していない場合は、この方法を試してください。面倒で回避策ですが、動作するはずです。 #tempテーブルとテーブル変数は、spによって返されるものの構造で設定する必要があります。
create table #templog (fie1d1 int, field2 varchar(10))
declare @templog table (fie1d1 int, field2 varchar(10))
BEGIN TRANSACTION
insert into #templog
Exec my_proc
insert into @templog (fie1d1, field2)
select t.* from #templog t
left join @templog t2 on t.fie1d1 = t2.fie1d1 where t2.fie1d1 is null
insert into templog
values (1, 'test')
rollback tran
select * from #templog
select * from templog
select * from @templog
ログの挿入をトランザクション外に移動するのは簡単な方法ではありませんか?
テーブルロックについての回答は実際にはありません。既に回答があると思います。 ID列はロールバックされる場合があります。
最初の挿入の後にBEGIN TRANSACTIONステートメントを移動します。
おそらく、独自のアトミックトランザクションt1でビジネステーブルへの挿入/更新を行い、これらの各トランザクションを、ロールバックなしでログテーブルの更新とt1(ビジネステーブルの更新)を実行する別のトランザクションt2でラップできます。例:
BEGIN TRANSACTION t2
<insert to log>
<execute stored procedure p1>
END TRANSACTION t2
CREATE PROCEDURE p1
AS
BEGIN TRANSACTION t1
<insert to business tables>
<rollback t1 on error>
END TRANSACTION t1
ストアドプロシージャでt1をロールバックすると、呼び出しトランザクションt2は影響を受けないままになると思います。