2

我遇到了一个与 SQL 相关的问题,现在让我很生气:p。这是我的设置:

我有 2 个存储过程:

父存储过程被调用GenerateAnnualPenalty

GenerateAnnualPenalty其中有一个 SELECT CURSOR,它遍历一系列名为 的对象Properties,并且对于每个Property,它确定是否Penalty需要应用 a 。这存储在一个名为 的布尔变量中@ApplyPenalty,以及它的0or 或1。此外,在GenerateAnnualPenalty.

其次,对于每个Property迭代GenerateAnnualPenalty,它调用一个名为的子存储过程GenerateAnnualPenaltyForProperty@ApplyPenalty作为输入传递给GenerateAnnualPenaltyForPropertyGenerateAnnualPenaltyForProperty确实使用 SQL 事务(提交/回滚)。

我在名为 的表中输入条目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

要迭代的第二个属性:@PROPERTY_ID = 2

要迭代的第三个属性:@PROPERTY_ID = 3

要迭代的第四个属性:@PROPERTY_ID = 4

假设对于属性 1、3 和 4,ApplyPenalty = 1,而对于属性 2,ApplyPenalty = 0

问题(主要症结):

GenerateAnnualPenaltyForProperty调用属性 1 时,一切都很好:我在表中看到了检查点 1 和检查点 2 条目DebugLog

GenerateAnnualPenaltyForProperty调用属性 2 时,一切都恢复正常:我在表中同时看到检查点 1 和检查点 2 条目DebugLog

GenerateAnnualPenaltyForProperty调用属性 3 时,会出现不正确的情况:我只看到表中的检查点 1 条目DebugLog,而“检查点 2”条目也应该是可见的!

GenerateAnnualPenaltyForProperty调用属性 4 时,它再次正确:我再次在表中看到检查点 1 和检查点 2 条目DebugLog

因此,问题仅发生在 的 属性中ApplyPenalty = 1,前提是在之前的迭代中,ApplyPenalty = 0。在这种情况下,带有的属性ApplyPenalty = 1会被视为ApplyPenalty = 0

禁用 SQL 事务代码可以解决问题,但为什么?:

如果我禁用所有GenerateAnnualPenaltyForProperty与 SQL 事务相关的代码,一切正常!纠正了上述有问题的情况。这是一个框架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 Transaction commit/rollback intoGenerateAnnualPenaltyForProperty时,存储过程不适用于有问题的情况?

实际代码:

如果有人希望查看子存储过程的完整代码,则可在此处获得:https ://gist.github.com/anonymous/5214236

4

2 回答 2

0

一个可能的解释是您有嵌套事务 - 例如,如果主逻辑调用一个存储过程,该过程也执行 BEGIN TRAN...COMMIT/ROLLBACK TRAN。如果内部事务执行 ROLLBACK,它将回滚到最外面的事务,因此在 Penalty 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 回答