2

私はストアドプロシージャを作成中です。このストアド プロシージャは、ローカルおよび外部ストアド プロシージャを実行します。簡単にするために、ローカル サーバー[LOCAL]とリモート サーバーを呼び出します[REMOTE]

簡単なトポロジを次に示します。

手順

USE [LOCAL]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[monthlyRollUp] 
AS
SET NOCOUNT, XACT_ABORT ON
BEGIN TRY
    EXEC [REOMTE].[DB].[table].[sp]

    --This transaction should only begin if the remote procedure does not fail
    BEGIN TRAN
        EXEC [LOCAL].[DB].[table].[sp1]
    COMMIT

    BEGIN TRAN
        EXEC [LOCAL].[DB].[table].[sp2]
    COMMIT

    BEGIN TRAN
        EXEC [LOCAL].[DB].[table].[sp3]
    COMMIT

    BEGIN TRAN
        EXEC [LOCAL].[DB].[table].[sp4]
    COMMIT
END TRY
BEGIN CATCH
    -- Insert error into log table
    INSERT INTO [dbo].[log_table] (stamp, errorNumber, 
        errorSeverity, errorState, errorProcedure, errorLine, errorMessage)
    SELECT GETDATE(), ERROR_NUMBER(), ERROR_SEVERITY(), ERROR_STATE(), ERROR_PROCEDURE(),
        ERROR_LINE(), ERROR_MESSAGE()
END CATCH
GO

リモート プロシージャでトランザクションを使用すると、次のエラーがスローされます。

OLE DB プロバイダ ... メッセージ「パートナー トランザクション マネージャは、リモート/ネットワーク トランザクションのサポートを無効にしました。」を返しました。

リモート プロシージャのトランザクションをローカルで実行できないことがわかりました。

手順の一部が失敗した場合、この手順が終了してロールバックすることを確認するにはどうすればよいですか?

ノート

  • 簡単な手順の組み合わせに関しては、一部は個別に使用されます。
4

4 に答える 4

4

IMOの最も簡単な方法は

  • リモート プロシージャに戻り値を追加します。
  • リモート プロシージャをトランザクションにラップし、(リモート プロシージャ内で) catch を試行します。エラーが発生した場合は false を返します。
  • ローカル ストアド プロシージャで false の場合は、続行しないでください。

また、ローカル プロシージャで複数の BEGIN TRANS / COMMIT の背後にある理由を理解できません。これが月末のロールアップである場合、これは小さな取引ではなく、1 つの大きな取引であるべきではないでしょうか? そうしないと、トランス 1 と 2 は正常にコミットできますが、3 は失敗し、それだけです。

名前は次のもので構成されます。

 CREATE PROC [remote].db.REMOTE_PROC ( 
      @return_value int output
 ) 
 AS 
 BEGIN 
      SET XACT_ABORT ON; 
      BEGIN TRY      
           BEGIN TRANS 
           ... do stuff ... 

                set @return_value = 1;
           COMMIT; 
      END TRY 
      BEGIN CATCH 
           set @return_value = 0; 
      END CATCH
 END 

およびローカル プロシージャ

 CREATE PROC [local].db.[monthlyRollUp] AS
 BEGIN 
      SET XACT_ABORT ON; 

      declare @ret int; 

      EXECUTE [remote].dbo.REMOTE_PROC @return_value = @ret OUTPUT; 

      IF @ret = 0 
           PRINT 'ERROR :(' 
           RETURN
      END IF 

      BEGIN TRANS 
           -- one big transaction here 
           EXEC [LOCAL].[DB].[table].[sp1]; 

           EXEC [LOCAL].[DB].[table].[sp2]; 

           EXEC [LOCAL].[DB].[table].[sp3]; 

           EXEC [LOCAL].[DB].[table].[sp4]; 

      COMMIT; 

 END; 

afair [remote].dbo.REMOTE_PROC は独自のトランザクション スペースを実行し、成功すると 1 を返します。ローカル プロシージャは、戻り値をチェックし、続行するかどうかを決定します。

sp1 sp2 sp3 と sp4 はすべて 1 つの単一トランザクションで実行されます。それぞれに複数のトランザクションを使用することはあまり意味がありません。

于 2012-11-30T11:02:07.407 に答える
0

私はあなたが何を望んでいるのか少し不明確かもしれません。リモート手順またはローカル手順のいずれかが失敗したときにロールバックするためにmonthlyRollUpSP全体が必要な場合は、分散トランザクションコーディネーターが必要になります。これにより、サーバーはトランザクションに関する情報を伝達し、コミットを調整できるようになります。つまり、両方のサーバーは、必要なすべてのロックが取得されたことを示してから、操作が自動になるように両方のサーバーでコミットを調整する必要があります。DTCの設定の一例を次に示します。http: //social.msdn.microsoft.com/forums/en-US/adodotnetdataproviders/thread/7172223f-acbe-4472-8cdf-feec80fd2e64/

リモートプロシージャがトランザクションに参加/影響を与えたくない場合は、次の設定を試すことができます。

SET REMOTE_PROC_TRANSACTIONS OFF;

http://msdn.microsoft.com/en-us/library/ms178549%28SQL.90%29.aspx

私は以前にその設定を使用したことがないので、それがあなたが必要とするものを達成するかどうかはわかりません。

于 2012-11-30T00:03:01.333 に答える
0

DTC を使用できない、または使用したくない場合、および CLR を使用したくない場合は、リモート sp 呼び出しをロールバックできないため、最後にリモート sp を呼び出す必要があります。

SET NOCOUNT, XACT_ABORT ON
SET REMOTE_PROC_TRANSACTIONS OFF;
BEGIN TRY
    DECLARE @ret INT
    BEGIN TRAN
        --Perform these in a transaction, so they all rollback together
        EXEC [LOCAL].[DB].[table].[sp1]
        EXEC [LOCAL].[DB].[table].[sp2]
        EXEC [LOCAL].[DB].[table].[sp3]
        EXEC [LOCAL].[DB].[table].[sp4]
    --We call remote sp last so that if it fails we rollback the above transactions
    --We'll have to assume that remote sp takes care of itself on error.
    EXEC [REMOTE].[DB].[table].[sp] 

    COMMIT
END TRY
BEGIN CATCH
    --We rollback
    ROLLBACK       
    -- Insert error into log table
    INSERT INTO [dbo].[log_table] (stamp, errorNumber, 
        errorSeverity, errorState, errorProcedure, errorLine, errorMessage)
    SELECT GETDATE(), ERROR_NUMBER(), ERROR_SEVERITY(), ERROR_STATE(),ERROR_PROCEDURE(),
    ERROR_LINE(), ERROR_MESSAGE()

END CATCH

ローカル sp がリモート ストアド プロシージャの結果に依存する場合は、CLR sp を使用して (EXTERNAL_ACCESS 権限が必要です)、トランザクションを明示的に管理できます (基本的に、独自の DTC をロールしますが、2 フェーズ コミットは行いません。リモートコミットを実質的に遅らせます。)

//C# fragment to roll your own "DTC"  This is not true two-phase commit, but 
//may be sufficient to meet your needs.  The edge case is that if you get an error
//while trying to commit the remote transaction, you cannot roll back the local tran.
using(SqlConnection cnRemote = new SqlConnection("<cnstring to remote>")) 
{
  try {

    cnRemote.Open();
    //Start remote transaction and call remote stored proc
    SqlTransaction trnRemote = cnRemote.BeginTransaction("RemoteTran");
    SqlCommand cmdRemote = cnRemote.CreateCommand();
    cmdRemote.Connection = cnRemote;
    cmdRemote.Transaction = trnRemote;
    cmdRemote.CommandType = CommandType.StoredProcedure;
    cmdRemote.CommandText = '[dbo].[sp1]';
    cmdRemote.ExecuteNonQuery();

        using(SqlConnection cnLocal = new SqlConnection("context connection=true")) 
        {
            cnLocal.Open();
            SqlTransaction trnLocal = cnLocal.BeginTransaction("LocalTran");
            SqlCommand cmdLocal = cnLocal.CreateCommand();
            cmdLocal.Connection = cnLocal;
            cmdLocal.Transaction = trnLocal;

            cmdLocal.CommandType = CommandType.StoredProcedure;
            cmdLocal.CommandText = '[dbo].[sp1]';
            cmdLocal.ExecuteNonQuery();
            cmdLocal.CommandText = '[dbo].[sp2]';
            cmdLocal.ExecuteNonQuery();
            cmdLocal.CommandText = '[dbo].[sp3]';
            cmdLocal.ExecuteNonQuery();
            cmdLocal.CommandText = '[dbo].[sp4]';
            cmdLocal.ExecuteNonQuery();

            //Commit local transaction
            trnLocal.Commit();

        }
        //Commit remote transction
        trnRemote.Commit();
    } // try
    catch (Exception ex)
    {
        //Cleanup stuff goes here.  rollback remote tran if needed, log error, etc.
    }
} 
于 2012-12-04T17:22:12.927 に答える
0

両方のストアド プロシージャを別のブロックに実行し、対応する CATCH ブロックTRY CATCHをチェックすることができます。ERROR_NUMBERERROR_NUMBER が取得しているエラーと同じである場合は、単にreturn、またはraiseerror要件に従って行うことができます。

致命的なエラーを引き起こしていますか。例外のエラー重大度を確認してください。

于 2012-11-28T09:37:29.263 に答える