我现在遇到一个与SQL相关的问题,现在只是惹恼我:p。这是我的设置:
我有2个存储过程:
父存储过程称为GenerateAnnualPenalty
。
GenerateAnnualPenalty
中有一个SELECT CURSOR,它迭代一系列名为Properties
的对象,并且对于每个Property
,它确定Penalty
是否需要应用。它存储在名为@ApplyPenalty
的布尔变量中,其0
或1
。此外,GenerateAnnualPenalty
中没有使用SQL事务。
其次,对于Property
迭代的每个GenerateAnnualPenalty
,它会调用名为GenerateAnnualPenaltyForProperty
的子存储过程。 @ApplyPenalty
作为GenerateAnnualPenaltyForProperty
的输入传递。 GenerateAnnualPenaltyForProperty
确实使用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
当为属性1调用GenerateAnnualPenaltyForProperty
时,一切都很好:我在DebugLog
表中看到了Checkpoint 1和Checkpoint 2条目。
当为属性2调用GenerateAnnualPenaltyForProperty
时,一切都很好:我在DebugLog
表中看到了Checkpoint 1和Checkpoint 2条目。
当为属性3调用GenerateAnnualPenaltyForProperty
时,会出现错误的情况:我只看到DebugLog
表中的Checkpoint 1条目,而'Checkpoint 2`条目也应该可见! / p>
当为属性4调用GenerateAnnualPenaltyForProperty
时,它再次正确:我再次看到DebugLog
表中的Checkpoint 1和Checkpoint 2条目。
所以问题只发生在ApplyPenalty = 1
的属性上,前提是在前一次迭代中ApplyPenalty = 0
。在这种情况下,ApplyPenalty = 1
的属性会被视为ApplyPenalty = 0
如果我禁用GenerateAnnualPenaltyForProperty
中与SQL Transactions相关的所有代码,一切正常!纠正了上述问题情况。这是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事务提交/回滚到GenerateAnnualPenaltyForProperty
时,存储过程对于有问题的情况不起作用?
如果有人希望查看子存储过程的完整代码,则可在此处找到:https://gist.github.com/anonymous/5214236
答案 0 :(得分: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
如果在事务期间确实发生了错误,并且回滚完成,您将看到导致它的错误。
答案 1 :(得分:0)
可能的解释是你有嵌套的事务 - 例如,如果主逻辑调用一个存储过程,它也执行BEGIN TRAN ... COMMIT / ROLLBACK TRAN。如果内部事务执行ROLLBACK,它将回滚到最外面的事务,因此在Penalty 3的情况下代码似乎失败。当你删除最外面的事务时,它只会回滚内部事务,使它看起来有效。