5

これは元の質問からの大幅な編集であり、より簡潔にし、既存の回答によって提起されたポイントをカバーしています...

単一のトランザクション内で複数のテーブルに複数の変更を加え、一部の変更のみをロールバックすることは可能ですか?

以下の 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 間で呼び出す必要があるため) それらがたまたま 1 つの内部にあるというのは不都合な事実です。

編集:

最終的な答えは「いいえ」です。唯一のオプションは、コードを再設計することです。ロギングにテーブル変数を使用するか (変数はロールバックされないため)、トランザクションを必要としないようにビジネス ロジックを再設計します...

4

7 に答える 7

5

SAVEPOINTsを使用します。

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
于 2009-03-06T14:00:00.653 に答える
2

ログエントリをテーブル変数に入れ、コミットまたはロールバック後に実際のテーブルに挿入することができました。

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
于 2009-03-06T14:46:14.193 に答える
2

基本的に、現在のコンテキストの外にジャンプする必要があります。それにはいくつかの方法があります。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
于 2009-03-06T14:46:58.263 に答える
1

SAVEPOINTSおよびTRANSACTION ISOLATION LEVELSを使用します。

于 2009-03-06T14:20:27.033 に答える
0

おそらく、ビジネステーブルへの挿入/更新を独自のアトミックトランザクション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は影響を受けないままになると思います。

于 2009-03-06T15:17:38.503 に答える
0

ログの挿入をトランザクションの外に移動するのが簡単な方法ではないでしょうか?

テーブルロックについての答えは本当にありません。すでに答えがあると思います。ID列がロールバックする可能性があるため、テーブルロックが必要になります。

于 2009-03-06T13:59:33.293 に答える
0

BEGIN TRANSACTION ステートメントを最初の挿入の後に移動します。

于 2009-03-06T14:00:19.733 に答える