繁体   English   中英

为什么此SQL Server事务总是失败,以及如何修复存储过程的结构?

[英]Why does this SQL Server transaction always fail and how to fix the stored procedure structure?

我在存储过程中遇到麻烦,而事务总是失败。 我认为这是由于IF EXISTS(SELECT 1 FROM TargetTable WHERE BaseTableId = @BaseTableId)或结构化代码的方式,但是我不确定如何修复存储过程,因此事务不会总是失败。 当我删除所有事务代码并在存储过程中没有事务的情况下执行相同的活动时,它将起作用。 所有更新和/或插入均成功。

CREATE PROCEDURE [dbo].[proc_BaseTable_TargetTable_Update] 
(
@BaseTableId        bigint,
@BaseTableTypeId        bigint,
@Alias      nvarchar(max),
@TargetTableNonPkItemId bigint,
@OtherField     nvarchar(max) = NULL
)
AS
-- NOTE: There is a problem with enabling tractions on this, the trans always fails
-- with the current structure, seemingly because of the SELECT 1 FROM TARGETTABLE
BEGIN
BEGIN TRAN

    UPDATE BaseTable 
        SET 
            Alias = @Alias,
            BaseTableTypeId = @BaseTableTypeId,
            UpdatedOn = GETUTCDATE()
    WHERE BaseTableId = @BaseTableId

    IF @@ERROR <> 0
    BEGIN
        RAISERROR('Error updating BaseTable', 16, 1)
        ROLLBACK TRAN
        RETURN
    END

    IF EXISTS(SELECT 1 FROM TargetTable WHERE BaseTableId = @BaseTableId)
        UPDATE TargetTable
            SET
                TargetTableNonPkItemId = @TargetTableNonPkItemId,
                OtherField = @OtherField
        WHERE BaseTableId = @BaseTableId

        IF @@ERROR <> 0
        BEGIN
            RAISERROR('Error updating TargetTable', 16, 1)
            ROLLBACK TRAN
            RETURN
        END
    ELSE
        INSERT INTO TargetTable(BaseTableId, TargetTableNonPkItemId, OtherField)
        VALUES (@BaseTableId, @TargetTableNonPkItemId, @OtherField)

        IF @@ERROR <> 0
        BEGIN
            RAISERROR('Error inserting TargetTable', 16, 1)
            ROLLBACK TRAN
            RETURN
        END

COMMIT TRAN
END

您需要包装IF 您的代码中发生的事情是您期望这样的事情

IF EXISTS
{
   UPDATE
   RAISE ERROR IF BAD
}
ELSE
{
    INSERT
    RAISE ERROR IF BAD
}

但是相反,你最终会得到这个

IF EXISTS
    UPDATE


IF ERROR
    RAISE ERROR
ELSE 
    INSERT


IF ERROR
    RAISE ERROR

因此,代码如下所示:

CREATE PROCEDURE [dbo].[proc_BaseTable_TargetTable_Update] 
(
@BaseTableId        bigint,
@BaseTableTypeId        bigint,
@Alias      nvarchar(max),
@TargetTableNonPkItemId bigint,
@OtherField     nvarchar(max) = NULL
)
AS
-- NOTE: There is a problem with enabling tractions on this, the trans always fails
-- with the current structure, seemingly because of the SELECT 1 FROM TARGETTABLE
BEGIN
BEGIN TRAN

    UPDATE BaseTable 
        SET 
            Alias = @Alias,
            BaseTableTypeId = @BaseTableTypeId,
            UpdatedOn = GETUTCDATE()
    WHERE BaseTableId = @BaseTableId

    IF @@ERROR <> 0
    BEGIN
        RAISERROR('Error updating BaseTable', 16, 1)
        ROLLBACK TRAN
        RETURN
    END

    IF EXISTS(SELECT 1 FROM TargetTable WHERE BaseTableId = @BaseTableId)
    BEGIN
        UPDATE TargetTable
            SET
                TargetTableNonPkItemId = @TargetTableNonPkItemId,
                OtherField = @OtherField
        WHERE BaseTableId = @BaseTableId

        IF @@ERROR <> 0
        BEGIN
            RAISERROR('Error updating TargetTable', 16, 1)
            ROLLBACK TRAN
            RETURN
        END
    END
    ELSE
    BEGIN
        INSERT INTO TargetTable(BaseTableId, TargetTableNonPkItemId, OtherField)
        VALUES (@BaseTableId, @TargetTableNonPkItemId, @OtherField)

        IF @@ERROR <> 0
        BEGIN
            RAISERROR('Error inserting TargetTable', 16, 1)
            ROLLBACK TRAN
            RETURN
        END
    END

COMMIT TRAN
END

您应该在IF之后和ELSE之后添加BEGIN / END块,因为这些块中有多个语句。

我更喜欢使用TRY CATCH进行错误处理。 下面的示例将给出更有用的错误消息。

CREATE PROCEDURE [dbo].[proc_BaseTable_TargetTable_Update]  
( @BaseTableId        bigint
, @BaseTableTypeId        bigint
, @Alias      nvarchar(max)
, @TargetTableNonPkItemId bigint
, @OtherField     nvarchar(max) = NULL ) 
AS  

BEGIN TRY
BEGIN TRAN      
    UPDATE BaseTable          
    SET Alias = @Alias
    , BaseTableTypeId = @BaseTableTypeId
    , UpdatedOn = GETUTCDATE()     
    WHERE BaseTableId = @BaseTableId           

IF EXISTS(SELECT 1 FROM TargetTable WHERE BaseTableId = @BaseTableId)         
    UPDATE TargetTable             
    SET TargetTableNonPkItemId = @TargetTableNonPkItemId
        , OtherField = @OtherField         
    WHERE BaseTableId = @BaseTableId         
ELSE         
    INSERT INTO TargetTable(BaseTableId, TargetTableNonPkItemId, OtherField)         
    VALUES (@BaseTableId, @TargetTableNonPkItemId, @OtherField)  

IF @@TRANCOUNT > 0
    BEGIN
        COMMIT TRAN
    END 
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;

IF @@TRANCOUNT > 0
    BEGIN
        ROLLBACK TRAN
    END

END CATCH

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM