【问题标题】:SQL Stored Procedure not handling error from nested stored procedureSQL 存储过程不处理来自嵌套存储过程的错误
【发布时间】:2014-08-01 16:15:44
【问题描述】:

我有两个存储过程,一个嵌套在另一个内部。目前,当调用嵌套存储过程时,它应该因违反外键约束而出错,然后回滚先前的调用以插入到 ProductLicense 表中。由于外键冲突,嵌套过程不会对数据库执行任何操作,但调用存储过程没有捕获错误并回滚。如果我自己执行嵌套存储过程,它会返回错误 547 外键违规。

如何让这两个存储过程一起工作?

外部程序:

ALTER PROCEDURE [dbo].[AddNewLicense2_i]
    -- Add the parameters for the stored procedure here
    @customerId nvarchar(10),
    @licenseModeId int,
    @licenseModeProgramId int,
    @createdBy int,
    @updateBy int,
    @systemId nvarchar(50),
    @productId int

AS
BEGIN TRY
    BEGIN TRANSACTION
        -- SET NOCOUNT ON added to prevent extra result sets from
        -- interfering with SELECT statements.
        SET NOCOUNT ON;
        --SET XACT_ABORT ON;  --used for automatic rollback when an error occurs    

        DECLARE @tempDays INT
        DECLARE @programCornerAmt INT
        DECLARE @tempEndDate DATETIME
        DECLARE @tempExpDate DATETIME
        DECLARE @err INT

        SET @err = 0

        /*SET @tempDays = (SELECT lmp.TimeoutDays
                         FROM LicenseModeProgram lmp 
                         WHERE lmp.LicenseModeProgramId = @licenseModeProgramId)*/

        SELECT @tempDays = TimeoutDays, @programCornerAmt = MonthlyCornersAmount
        FROM LicenseModeProgram
        WHERE LicenseModeProgramId = @licenseModeProgramId

        --Build Expiration and End Dates.
        IF @tempDays = NULL --then this is NOT a time rental or metered system
            BEGIN
                SET @tempEndDate = NULL
                SET @tempExpDate = NULL
            END
        ELSE
            BEGIN
                SET @tempEndDate = DATEADD("d", @tempDays, GETDATE())
                SET @tempExpDate = DATEADD("d", @tempDays, GETDATE())
            END 

        -- Create new product license record
        INSERT INTO ProductLicense (CustomerId, LicenseModeId, LicenseModeProgramId, CreatedBy, UpdatedBy, SystemId, ProductId, ExpirationDate, LicenseEndDate)
        VALUES (@customerId, @licenseModeId, @licenseModeProgramId, @createdBy, @updateBy, @systemId, @productId, @tempExpDate, @tempEndDate)

        IF @licenseModeId = 4 AND @systemId  NULL AND @programCornerAmt  NULL
            --call stored procedure to add corners to the customer account
            EXECUTE @err = AddMeteredTx_i @systemId, 1, 1, @programCornerAmt , 'Initial License Creation'

        PRINT @err

    COMMIT TRANSACTION
END TRY
BEGIN CATCH 
    RAISERROR('Failed to Create License', 11, 2)
    ROLLBACK TRANSACTION
    RETURN 1
END CATCH

    --COMMIT TRANSACTION
RETURN 0
GO

内部程序:

ALTER PROCEDURE [dbo].[AddMeteredTx_i]
    -- Add the parameters for the stored procedure here

    @systemId nvarchar(50),
    @activityEventId int,
    @createdBy int,
    @amount int,
    @notes text
AS
BEGIN TRY
    BEGIN TRANSACTION
        -- SET NOCOUNT ON added to prevent extra result sets from
        -- interfering with SELECT statements.
        SET NOCOUNT ON;
        --SET XACT_ABORT ON;  --used for automatic rollback when an error occurs

        INSERT INTO CustomerAccountActivity (SystemId, ActivityEventId, CreatedBy, Amount, Notes)
        VALUES (@systemId, @activityEventId, @createdBy, @amount, @notes)

        UPDATE CustomerAccount
        SET MeteredBalance = (SELECT MeteredBalance FROM CustomerAccount WHERE SystemId = @systemId) + @amount
        WHERE SystemId = @systemId

    COMMIT TRANSACTION
END TRY
BEGIN CATCH

    RAISERROR('Error Update to Customer Account Record ', 11, 2)
    ROLLBACK TRANSACTION
    RETURN 1

    --COMMIT TRANSACTION
END CATCH

RETURN 0
GO

【问题讨论】:

  • 我们还需要查看外部过程的代码。
  • 另外,请避免使用@@ERROR 来检测错误,TRY/CATCH 更加健壮。
  • 对不起,我刚刚添加了它。

标签: sql sql-server stored-procedures


【解决方案1】:

使用@@Error 这样的调用堆栈捕获错误可能会出现问题。使用TRY/CATCH可靠很多

基本格式是:

BEGIN TRY
<BEGIN TRAN>

... do stuff ...
<COMMIT TRAN>
END TRY
BEGIN CATCH
<ROLLBACK TRAN>
... do error stuff like re-raise the error to outer scope ...


END CATCH

在尝试中遇到的任何错误都会自动将您带到 CATCH 块,而无需额外检查。

【讨论】:

  • 所以你建议没有引发外部约束违规的原因是因为我没有使用 try/catch 块?
  • 是的,可能。在错误之前执行的最后一个命令实际上是 ROLLBACK。 @@ERROR 非常非常不可靠。连 MS 都说不要用。
  • 我按照您的建议更新了存储过程,但仍然没有发现错误。
  • 在内部过程中,一旦您重新引发错误,它就会升级到外部范围内的捕获。尝试在那里重新排序您的流程。
  • 仍然没有区别。当我将内部过程作为单个执行存储过程运行时,错误将返回到结果窗口。为什么它没有返回到调用过程。这就是为什么我声明 @err 只是为了捕获返回值以查看它是否会起作用。
猜你喜欢
  • 1970-01-01
  • 2013-05-17
  • 1970-01-01
  • 2011-12-07
  • 1970-01-01
  • 2020-12-11
  • 1970-01-01
  • 1970-01-01
  • 2011-04-02
相关资源
最近更新 更多