質問

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

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

以下の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は影響を受けないままになると思います。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top