4

私はSQLServer 2014を使用しており、IDとデータと呼ばれるvarchar列を持つ1つのテーブルを持つ単純なデータベースを持っています。次のステートメントを実行すると、奇妙な動作が発生します。

SET XACT_ABORT ON
BEGIN TRANSACTION
    exec sp_executesql N'some nonsense'
    insert into testTable values ('b')

COMMIT  

SSMS は、呼び出しで間違ったクエリを実行しようとしたため、エラーが発生したことを示していますsp_executesql。ただし、 も示しています1 row(s) affected。testTable で選択クエリを実行すると、値 'b' が挿入されていることがわかります。

ステートメントをTRY/CATCHブロックでラップすると、すべてが期待どおりに機能し、トランザクション アクション全体がロールバックされます。

BEGIN TRANSACTION
BEGIN TRY
    exec sp_executesql N'some nonsense'
    insert into testTable values ('b')

    COMMIT
END TRY
BEGIN CATCH
    SELECT ERROR_MESSAGE()
    ROLLBACK
END CATCH

SET XACT_ABORT ON何か問題が発生した場合、トランザクション全体がロールバックされることを保証するべきではありませんか? 不足している設定はありますか?

ありがとう

4

2 に答える 2

4

TRYこれは、 /でラップされていない実行時構文エラーが、に設定されてCATCHいても、アクティブなトランザクションを中止しないために発生します。何が中止され、何が中止されないか、およびどのような状況下で行われるかについての正確な規則は、まったく単純でも明白でもありません。Erland Sommarskog は、一般的なエラー処理と、特に異常終了するものとしないものの規則について優れた記事を書いてますXACT_ABORTON

ここですべてを再現することはしませんが、問題を要点に要約すると次のようになります。

SET XACT_ABORT ON   -- or OFF, it makes no difference
BEGIN TRANSACTION
EXEC ('SELECT')     -- Incorrect syntax near 'SELECT'
PRINT @@TRANCOUNT   -- Prints 1, transaction is still going
COMMIT
PRINT @@TRANCOUNT   -- Prints 0, transaction succeeded

にもかかわらずXACT_ABORT ON、実行が停止しないだけでなく、トランザクションが中止されることさえありません。ルールの追加TRY/変更:CATCH

SET XACT_ABORT ON
BEGIN TRANSACTION
BEGIN TRY
    EXEC ('SELECT')              -- Incorrect syntax near 'SELECT'
    PRINT 'After bad statement.' -- Does not print
    COMMIT
END TRY
BEGIN CATCH
    PRINT @@TRANCOUNT            -- Prints 1, transaction is still going, but it's doomed
END CATCH
-- Error here: 
-- 'Uncommittable transaction is detected at the end of the batch. 
--  The transaction is rolled back.'

これで、トランザクションは運命づけられました。自分でロールバックしないと、SQL Server が代わりに (エラーで) ロールバックします。XACT_ABORTこれをオフにすると、また別の結果が得られるため、この運命は完全に の好意によるものです。

SET XACT_ABORT OFF
BEGIN TRANSACTION
BEGIN TRY
    EXEC ('SELECT')               -- Incorrect syntax near 'SELECT'
    PRINT 'After bad statement.'  -- Does not print
    COMMIT
END TRY
BEGIN CATCH
    PRINT @@TRANCOUNT             -- Prints 1, transaction is still going
END CATCH
PRINT @@TRANCOUNT                 -- Prints 1, transaction is still going!
ROLLBACK

この話の教訓は、T-SQL での適切なエラー処理は非常に難しいということです。通常、私にとってうまくいくのは、SET XACT_ABORT ON重要なステートメントのバッチに対して実行し、トランザクションを SQL Server の外部で (クライアント コードを介して) 開始およびコミットまたはロールバックすることです。これにより、SQL Server がクライアントにエラーを返すと最終的にロールバックが発生するため、トランザクションを停止または破滅させるものとしないものを理解する際の困難の多くが回避されます。しかしもちろん、それでも特効薬ではありません。

于 2016-07-21T23:46:21.337 に答える