2

私は今、私を怒らせているSQL関連の問題を抱えています:p. これが私のセットアップです:

私は2つのストアドプロシージャを持っています:

親ストアド プロシージャが呼び出されGenerateAnnualPenaltyます。

GenerateAnnualPenaltyPropertiesには SELECT CURSOR があり、 と呼ばれる一連のオブジェクトを反復し、 を適用する必要があるPropertyかどうかを判断します。Penaltyこれは、 という名前のブール変数と、 または@ApplyPenaltyのいずれかに0格納されます1。また、 で使用されている SQL トランザクションはありませんGenerateAnnualPenalty

2 番目に、反復するそれぞれPropertyに対して、という名前の子ストアド プロシージャを呼び出します。への入力として渡されます。SQL トランザクション (コミット/ロールバック) を使用します。GenerateAnnualPenaltyGenerateAnnualPenaltyForProperty@ApplyPenaltyGenerateAnnualPenaltyForPropertyGenerateAnnualPenaltyForProperty

という名前のテーブルにエントリを作成してDebugLog、コード内の特定のポイントに到達したかどうかをマークします。

子のスケルトンは次のGenerateAnnualPenaltyForPropertyとおりです。

ALTER PROCEDURE [RTS].[GenerateAnnualPenaltyForProperty]
@PROPERTY_ID numeric(18,0),
@ApplyPenalty int
AS

insert into DebugLog (DebugMessage1, DebugMessage2, DebugMessage3, DebugMessage4)
values
('Checkpoint 1 for Property:', @PROPERTY_ID, 'Apply Penalty:', @ApplyPenalty)

DECLARE @TRANSACTION_NAME varchar(50)
SET @TRANSACTION_NAME = 'GenerateAnnualPenaltyForProperty'

BEGIN TRANSACTION @TRANSACTION_NAME

BEGIN TRY


    insert into DebugLog (DebugMessage1, DebugMessage2, DebugMessage3, DebugMessage4)
    values
    ('Checkpoint 2 for Property:', @PROPERTY_ID, 'Apply Penalty:', @ApplyPenalty)

    IF @ApplyPenalty = 1
    BEGIN
        -- All main logic here !!!
    END

    COMMIT TRANSACTION @TRANSACTION_NAME
    RETURN 0
END TRY
BEGIN CATCH
    ROLLBACK TRANSACTION @TRANSACTION_NAME
    RETURN -1
END CATCH

問題 (イントロ):

GenerateAnnualPenaltyこの特定の順序で4 を反復するとPropertiesします (これは問題を悪用します)。

反復する最初のプロパティ:@PROPERTY_ID = 1

反復する 2 番目のプロパティ:@PROPERTY_ID = 2

反復する 3 番目のプロパティ:@PROPERTY_ID = 3

反復する 4 番目のプロパティ:@PROPERTY_ID = 4

プロパティ 1、3、および 4 についてApplyPenalty = 1は 、プロパティ 2 については、ApplyPenalty = 0

問題 (主な核心):

プロパティ 1 に対して が呼び出されるとGenerateAnnualPenaltyForProperty、すべて問題ありません。チェックポイント 1 とチェックポイント 2 の両方のエントリがDebugLogテーブルに表示されます。

プロパティ 2 に対して が呼び出されるとGenerateAnnualPenaltyForProperty、すべて正常に戻ります。チェックポイント 1 とチェックポイント 2 の両方のエントリがDebugLogテーブルに表示されます。

プロパティ 3 に対して が呼び出されるとGenerateAnnualPenaltyForProperty、間違ったシナリオが発生します。テーブルにはチェックポイント 1 エントリしかDebugLog表示されませんが、'チェックポイント 2' エントリも表示されているはずです!

プロパティ 4 に対して が呼び出されるとGenerateAnnualPenaltyForProperty、再び正しい結果が得られます。チェックポイント 1 とチェックポイント 2 の両方のエントリがDebugLogテーブルに表示されます。

したがって、問題ApplyPenalty = 1は、前の反復でApplyPenalty = 0. このような場合、 のプロパティはApplyPenalty = 1あたかもそうであるかのように扱われます。ApplyPenalty = 0

SQL トランザクション コードを無効にすると問題は解決しますが、その理由は?:

SQL トランザクションに関連するすべてのコードを無効にするとGenerateAnnualPenaltyForProperty、すべて正常に動作します。上記の問題が修正されました。GenerateAnnualPenaltyForProperty以下は、動作する SQL トランザクション コードが取り出された のスケルトンです。

ALTER PROCEDURE [RTS].[GenerateAnnualPenaltyForProperty]
@PROPERTY_ID numeric(18,0),
@ApplyPenalty int
AS

insert into DebugLog (DebugMessage1, DebugMessage2, DebugMessage3, DebugMessage4)
values
('Checkpoint 1 for Property:', @PROPERTY_ID, 'Apply Penalty:', @ApplyPenalty)


BEGIN TRY
    insert into DebugLog (DebugMessage1, DebugMessage2, DebugMessage3, DebugMessage4)
    values
    ('Checkpoint 2 for Property:', @PROPERTY_ID, 'Apply Penalty:', @ApplyPenalty)

    IF @ApplyPenalty = 1
    BEGIN
        -- All main logic here !!!
    END

    RETURN 0
END TRY
BEGIN CATCH
    RETURN -1
END CATCH

質問 (???):

なぜこの動作が発生するのですか? SQL トランザクションの commit/rollback into を使用するGenerateAnnualPenaltyForPropertyと、問題のあるケースでストアド プロシージャが機能しないのはなぜですか?

実際のコード:

子ストアド プロシージャの完全なコードを見たい場合は、https ://gist.github.com/anonymous/5214236 から入手できます。

4

2 に答える 2

0

考えられる説明は、ネストされたトランザクションがあることです。たとえば、メイン ロジックが BEGIN TRAN...COMMIT/ROLLBACK TRAN も実行するストアド プロシージャを呼び出す場合などです。内部トランザクションが ROLLBACK を実行すると、最も外側のトランザクションにロールバックされるため、ペナルティ 3 の場合、コードは失敗したように見えます。最も外側のトランザクションを削除すると、内側のトランザクションのみがロールバックされ、機能しているように見えます。

于 2013-03-21T13:48:27.690 に答える
0

コードを次のように変更してみてください:

BEGIN TRY

    -- Your code

END TRY

BEGIN CATCH

    DECLARE @ErrorMessage NVARCHAR(MAX);
    DECLARE @ErrorSeverity INT;
    DECLARE @ErrorState INT;
    SELECT @ErrorMessage = ERROR_MESSAGE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE();
    RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState)

END CATCH

トランザクション中にエラーが発生し、ロールバックが行われた場合、その原因となったエラーが表示されます。

于 2013-03-21T13:24:32.830 に答える