web-dev-qa-db-ja.com

本当に「SETXACT_ABORTON」を使用する必要がありますか?

注意してすべての周りでTRY-CATCHを使用し、エラーのロールバックを使用する場合、本当に使用する必要がありますか?

SET XACT_ABORT ON

つまり、SET XACT_ABORT ONが処理するTRY-CATCHが見逃すエラーはありますか?

25
KM.

TRY-CATCHがXACT_ABORTの有無にかかわらずキャプチャしないエラーがあることに注意してください。

ただし、 SET XACT_ABORT ON はエラーのトラップには影響しません。ただし、トランザクションがロールバック/運命づけられることは保証されます。 「オフ」の場合でも、コミットまたはロールバックを選択できます(xact_stateに従います)。これは、SQL 2005 for XACT_ABORTの動作の主な変更点です。

また、クライアントコマンドのタイムアウトが発生し、クライアントが「abort」ディレクティブを送信した場合に、ロックなどを削除します。 SET XACT_ABORTがないと、接続が開いたままの場合にロックが残る可能性があります。私の同僚(MVP)と私は、今年の初めにこれを徹底的にテストしました。

35
gbn

分散トランザクションを実行するときは、SET XACT_ABORTONが要件だったと思います。

オンラインブックから: ほとんどのOLE SQLServerを含むDBプロバイダーに対する暗黙的または明示的なトランザクションのデータ変更ステートメントに対してXACT_ABORTをオンに設定する必要があります。唯一のケースこのオプションが不要なのは、プロバイダーがネストされたトランザクションをサポートしている場合です。詳細については、「分散クエリと分散トランザクション」を参照してください。

3

私の理解では、try catchが使用され、catchブロックでrollbackステートメントが使用されていない場合でも、XACT_ABORTがONの場合、コミットできないトランザクションはロールバックされます。

1
Satyajit

XACT_ABORTは実際にエラー処理に影響を与えます。エラーが発生するとバッチ全体を中止し、エラーを生成した行に続くコード(エラーチェックを含む)は実行されません。この動作には2つの例外があります。XACT_ABORTはTRY ... CATCHに置き換えられ(CATCHブロックは常に実行され、トランザクションは自動的にロールバックされず、コミット不能になるだけです)、XACT_ABORTはRAISERRORを無視します。

1

常にSET XACT_ABORT ONを盲目的に使用する場合の注意点があります。最近私を燃やした。

stackOverflowで説得力のある議論を読みました。これは、shouldは常にXACT_ABORT ONを使用する必要があることを示唆しています。接続中にそのオプションを設定するようにシステムを変更しました。それがデータの破損と多くの痛みにつながることを除いて。

begin transaction
try
    perform insert
    catch duplicate key violation and react appropriately

    perform more actions

    commit transaction
catch
    rollback transaction
end

「その他のアクション」がトランザクションで発生しなくなることを除いて。 重複キー違反をキャッチしたにもかかわらず、サーバーはトランザクションに参加していません。

begin transaction
try
    perform insert
    catch duplicate key violation and react appropriately
    transaction implicitly rolled back

    perform more actions

    commit transaction -> fails because not in a transaction
catch
    rollback transaction -> fails because not i a transaction
end

それ以来、私は自分自身を逆転させました。 決してSET XACT_ABORT ONを使用します。


編集:この問題は、トランザクションを行っていないときにROLLBACK TRANSACTIONを呼び出そうとしたことが原因であると考えられているようです。彼らは、トランザクションが進行中でない場合にROLLBACKを呼び出さないことで、問題を修正できると考えています。

NDAを保護するために名前を変更して、いくつかの擬似コードを使用してみましょう。

const
   SQLNativeErrorPrimaryKeyViolation = 2627; //Primary keys. 2601 is for other unique index

void x(String sql)
{
   database.Connection.ExecuteNoRecords(sql);
}

これは、この答えをより読みやすくするための衒学的な方法です。 xを使用して、SQLステートメントの実行を表します。

void DoStuff()
{
   x("BEGIN TRANSACTION");
   try
   {
      try
      {
         x("INSERT INTO Patrons (AccountNumber, Name, Gender)"+
           "VALUES (619, 'Shelby Jackson', 'W'"); 
      } 
      catch (ESqlServerException e)
      {
         //check if the patron already exists (or some other hypothetical situation arises)
         if (e.NativeError == SQLNativeErrorPrimaryKeyViolation)
         {
            //This patron already exists. Set their frob to grob because contoso the blingblong
            x("UPDATE Patrons SET Frob='Grob' WHERE AccountNumber = 619");

            //20110918: Dont forget we also need to bang the gardinker
            x("EXECUTE BangTheGardinker @floof=619");
         }
         else
            throw e;
      }

      //Continue with the stuff
      x("EXECUTE Frob('{498BBB4D-D9F7-4438-B7A6-4AB5D57937C0}')");

      //All done, commit the transaction
      x("COMMIT TRANSACTION");       
   }
   catch (Exception e)
   {
      //Something bad happened, rollback the transaction 
      //(if SQL Server didn't kill the transaction without our permission)
      x("IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION");


      throw e;
   }
}

XACT_ABORT ONはかっこいい、使ってみよう

したがって、そのコードは機能します。 expectというエラーが発生した場合は、それを処理して続行します。これはエラーの処理と呼ばれます。 不明例外が発生した場合(予期していなかったことが発生した場合)、進行中の可能性のあるトランザクションをrollbackします。

ここで、XACT_ABORTを常にオンにするという提案に盲目的に従うかどうかを見てみましょう。

 DbConnection Connection()
 {
    if (_connection == null)
    {
       _connection = new SqlConnection();

       //It is generally recommended that you always have xact_abort on.
       //If a connection is closed with a transaction still in progress
       //it still leaves locks held until that connection is finally recycled
       //Also, when querying linked severs in a client-side transaction, the
       //operation won't work until xact_abort is on (SQL Server will throw an saying xactabort is off
       _connection.ExecuteNoRecords("SET XACT_ABORT ON");
    }

    return _connection;
 }

void x(String sql)
{
   database.Connection.ExecuteNoRecords(sql);
}

DoStuffで発生する破損がわかりますか?

DoStuffエラーケースを処理するために正しく記述されました。ただし、接続にXACT_ABORT ONを導入すると、データベースが破損するようになります。バグが見当たらない方のために、コードを見ていきましょう。

void DoStuff()
{
   x("BEGIN TRANSACTION");
   try
   {
      try
      {
         x("INSERT INTO Patrons (AccountNumber, Name, Gender)"+
           "VALUES (619, 'Shelby Jackson', 'W'"); 

      } 
      catch (ESqlServerException e)
      {
         //WARNING: WE ARE NO LONGER OPERATING IN A TRANASCTION
         //Because XACT_ABORT is on, the transaction that we started has been implicitly rolled back.
         //From now on, we are no longer in a transaction. If another error happens
         //the changes we make cannot be rolled back

         //check if the patron already exists (or some other hypothetical situation arises)
         if (e.NativeError == SQLNativeErrorPrimaryKeyViolation)
         {
            //WARNING: This update happens outside of any transaction!
            //This patron already exist. Set their frob to grob because contoso the blingblong
            x("UPDATE Patrons SET Frob='Grob' WHERE AccountNumber = 619");

            //WARNING: This stored procedure happens outside of any transaction!
            //20110918: Dont forget we also need to bang the gardinker
            x("EXECUTE BangTheGardinker @floof=619");
         }
         else
            throw e;
      }

      //WARNING: This stored procedure happens outside of any transaction!
      //If any error happens from
      //Continue with the stuff
      x("EXECUTE Frob('{498BBB4D-D9F7-4438-B7A6-4AB5D57937C0}')");

      //WARNING: This stored procedure happens outside of any transaction. It will throw:
      //   Msg 3902, Level 16, State 1, Line 1
      //   The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.
      //All done, commit the transaction
      x("COMMIT TRANSACTION");       
   }
   catch (Exception e)
   {
      //If there was an error during Frob, we would want to catch it and roll everything back.
      //But since SQL Server ended the transaction, we have no way to rollback the changes

      //And even if the call to Frob (or Updating the patron's Grob, or Banging the Gardinder)
      //didn't fail, the call to COMMIT TRANSACTION will throw an error

      //Either way, we have detected an error condition that cannot be rolled back in the database


      //Something bad happened, rollback the transaction 
      //(if SQL Server didn't kill the transaction without our permission)
      x("IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION");


      throw e;
   }
}

正しく記述され、機能し、壊れ、エラーを引き起こし、最悪の場合、データベースの破損を引き起こすコード。 XACT_ABORT ONをオンにしたからです。

1
Ian Boyd

トリガーでXACT_ABORTがOFFに設定されていて、トリガー本体でRAISERRORを呼び出すと、変更はロールバックされません。

0