web-dev-qa-db-ja.com

TSQLトランザクション内での試行/キャッチ、またはその逆?

多くのテーブルからレコードを削除するスクリプトを書いていますが、削除する前に、ユーザーがコミットする前に確認するためのカウントを返す必要があります。

これはスクリプトの要約です。

BEGIN TRANSACTION SCHEDULEDELETE
    BEGIN TRY
        DELETE   -- delete commands full SQL cut out
        DELETE   -- delete commands full SQL cut out
        DELETE   -- delete commands full SQL cut out
        PRINT 'X rows deleted. Please commit or rollback.' --calculation cut out.
    END TRY
    BEGIN CATCH 
        SELECT
            ERROR_NUMBER() AS ErrorNumber,
            ERROR_SEVERITY() AS ErrorSeverity,
            ERROR_STATE() AS ErrorState,
            ERROR_PROCEDURE() AS ErrorProcedure,
            ERROR_LINE() AS ErrorLine,
            ERROR_MESSAGE() AS ErrorMessage

            ROLLBACK TRANSACTION SCHEDULEDELETE
            PRINT 'Error detected, all changes reversed.'
    END CATCH

--COMMIT TRANSACTION SCHEDULEDELETE --Run this if count correct.

--ROLLBACK TRANSACTION SCHEDULEDELETE --Run this if there is any doubt whatsoever.

トランザクションを作成するのはこれが初めてです。トランザクション内にTRY/CATCHブロックを含めることは正しい/ベストプラクティスですか、それともトランザクションをTRYブロック内に置くべきですか?

このスクリプトの重要な要素は、ユーザーがトランザクションを手動でコミットする必要があることです。

22
Devasta

TRYブロック内で実際のステートメントの直前になってからトランザクションを開き、すぐにコミットします。コントロールがバッチの最後に移動してトランザクションをコミットするのを待たないでください。

TRYブロックにいる間に何か問題が発生し、トランザクションを開いた場合、コントロールはCATCHブロックにジャンプします。トランザクションをそこでロールバックし、必要に応じて他のエラー処理を行うだけです。

トランザクションを実際にロールバックする前に、@@TRANCOUNT関数を使用して開いているトランザクションのチェックを少し追加しました。このシナリオではあまり意味がありません。 TRYブロックでいくつかの検証チェックを行ってから、パラメータ値やその他のものをチェックし、検証チェックのいずれかがあればTRYブロックでエラーを発生させるなどのトランザクションを開く場合に便利です。失敗します。その場合、コントロールはトランザクションを開くことなくCATCHブロックにジャンプします。そこで、開いているトランザクションをチェックし、開いているトランザクションがある場合はロールバックできます。あなたの場合、トランザクション内で何か問題が発生しない限り、CATCHブロックに入ることはないので、開いているトランザクションをチェックする必要はありません。

DELETE操作を実行した後、コミットまたはロールバックする必要があるかどうかを尋ねないでください。トランザクションを開く前にこれらすべての検証を行ってください。トランザクションが開かれたらすぐにコミットし、エラーが発生した場合はエラー処理を行います(ほとんどすべてのエラー関数を使用して詳細情報を取得することで、良い仕事をしています)。

BEGIN TRY

  BEGIN TRANSACTION SCHEDULEDELETE
    DELETE   -- delete commands full SQL cut out
    DELETE   -- delete commands full SQL cut out
    DELETE   -- delete commands full SQL cut out
 COMMIT TRANSACTION SCHEDULEDELETE
    PRINT 'X rows deleted. Operation Successful Tara.' --calculation cut out.
END TRY

BEGIN CATCH 
  IF (@@TRANCOUNT > 0)
   BEGIN
      ROLLBACK TRANSACTION SCHEDULEDELETE
      PRINT 'Error detected, all changes reversed'
   END 
    SELECT
        ERROR_NUMBER() AS ErrorNumber,
        ERROR_SEVERITY() AS ErrorSeverity,
        ERROR_STATE() AS ErrorState,
        ERROR_PROCEDURE() AS ErrorProcedure,
        ERROR_LINE() AS ErrorLine,
        ERROR_MESSAGE() AS ErrorMessage
END CATCH
46
M.Ali

M.ALiと上記の学部長による優れたアドバイスに加えて、SQL SERVERで新しい(新しい)TRY CATCH THROWパラダイムを使用することを検討している人にとっては、少し助けになります。

(完全な構文を簡単に見つけることができなかったので、ここに追加します)

要点: [〜#〜] here [〜#〜]

ここにサンプルストアドプロシージャコード(私の要点から):

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE PROC [dbo].[pr_ins_test]
@CompanyID INT
AS

SET NOCOUNT ON

BEGIN

    DECLARE @PreviousConfigID INT

    BEGIN TRY
        BEGIN TRANSACTION MYTRAN; -- Give the transaction a name
        SELECT 1/0  -- Generates divide by zero error causing control to jump into catch

        PRINT '>> COMMITING'
        COMMIT TRANSACTION MYTRAN;
    END TRY
    BEGIN CATCH
        IF @@TRANCOUNT > 0
        BEGIN 
            PRINT '>> ROLLING BACK'
            ROLLBACK TRANSACTION MYTRAN; -- The semi-colon is required (at least in SQL 2012)


        END
        THROW
    END CATCH
END
3

シングルユーザーモードのデータベースでない限り、エンドユーザーがトランザクションをコミットするのを待たないでください。

要するに、それはブロッキングについてです。トランザクションは、更新されるリソースに対していくつかの排他的ロックを取得し、トランザクションが終了(コミットまたはロールバック)するまでそれらのロックを保持します。誰もそれらの行に触れることができません。バージョンストアのクリーンアップでスナップショット分離が使用される場合、いくつかの異なる問題があります。

最初に選択クエリを発行して、条件を満たす行の数を決定し、これをエンドユーザーに提示し、確認後、実際の削除を行う方がよいでしょう。

2
dean