SQL Server 中的事务和@@Error 函数

Posted

技术标签:

【中文标题】SQL Server 中的事务和@@Error 函数【英文标题】:Transactions and @@Error function in SQL Server 【发布时间】:2020-09-22 03:36:30 【问题描述】:

我只是想检查一下我放置@@Error和Begin/Commit tran的位置是否正确?

我不确定是否应该使用 Begin Tran 而不是 DELETE 语句? @@ERROR 有什么意义吗?

谢谢!

CREATE PROCEDURE spDeleteAnInactiveEmployee
    @TrainerID int,
    @EActive char (1)
AS
    BEGIN TRY
    BEGIN TRAN

        IF (SELECT COUNT(*) FROM EmployeeDetails ed 
            WHERE TrainerID = @TrainerID) = 0
          RAISERROR ('Trainer details were not deleted. Trainer ID does not exist.', 16, 1)

        IF EXISTS (SELECT * FROM EmployeeDetails ed 
                   WHERE TrainerID = @TrainerID AND EActive = 'Y')
            RAISERROR ('Trainer details were not deleted. Trainer is still active.', 16, 1)

        DELETE FROM [EmployeeDetails]    
        WHERE TrainerID = @TrainerID AND EActive = 'N'

        IF @@ERROR = 0
            COMMIT TRAN

        BEGIN
            PRINT 'Employee ID' + CAST (@TrainerID AS VARCHAR) + ' was successfully deleted.'
        END
    END TRY
    BEGIN CATCH
        SELECT
            ERROR_NUMBER() AS ErrorNumber,
            ERROR_STATE() AS ErrorState,
            ERROR_SEVERITY() AS ErrorSeverity,
            ERROR_PROCEDURE() AS ErrorProcedure,
            ERROR_LINE() AS ErrorLine,
            ERROR_MESSAGE() AS ErrorMessage;

        IF (XACT_STATE()) = -1
        BEGIN  
            PRINT 'Transaction was not committed' 
            ROLLBACK TRANSACTION;  
        END;  

        IF (XACT_STATE()) = 1
        BEGIN 
            PRINT 'Transaction was committed'
            COMMIT TRANSACTION;
        END;
    END CATCH;
GO

【问题讨论】:

为什么在这里选择错误? THROWing 它不是更好吗?还有为什么COMMIT交易出错?一般来说,您想在出错时COMMIT 我没有看到@@ERROR 签入代码,只有正常的结构化处理。我建议 1) 在 proc 的开头指定 SET XACT_ABORT ON; 以确保在查询超时的情况下立即回滚 2) 将 catch 块更改为简化的 IF @@TRANCOUNT > 0 ROLLBACK; THROW; (假设 SQL 2012 或更高版本)以回滚 tran 和重新提出原来的错误。 对不起,我想说的是@@Error 而不是@@Rowcount。 does the @@ERROR make any sense at all - 不,因为在 try 块中,raiserror 将立即跳转到 catch。在try 块之外,它将继续并到达您的@@error 【参考方案1】:

当你使用 TRY/CATCH 时,@@ERROR 是不必要的。在 TRY/CATCH 之前,您必须在每个可能失败的语句之后检查 @@ERROR,并使用 GOTO 将控制流强制到错误标签。

所以这应该是这样的:

CREATE PROCEDURE spDeleteAnInactiveEmployee
    @TrainerID int,
    @EActive char (1)
AS
BEGIN
    SET XACT_ABORT ON;
    BEGIN TRY
        BEGIN TRAN

        IF (SELECT COUNT(*) FROM EmployeeDetails ed 
            WHERE TrainerID = @TrainerID) = 0
          RAISERROR ('Trainer details were not deleted. Trainer ID does not exist.', 16, 1)

        IF EXISTS (SELECT * FROM EmployeeDetails ed 
                   WHERE TrainerID = @TrainerID AND EActive = 'Y')
            RAISERROR ('Trainer details were not deleted. Trainer is still active.', 16, 1)

        DELETE FROM [EmployeeDetails]    
        WHERE TrainerID = @TrainerID AND EActive = 'N'

        COMMIT TRAN
        PRINT 'Employee ID' + CAST (@TrainerID AS VARCHAR) + ' was successfully deleted.'
    END TRY
    BEGIN CATCH
        IF @@TRANCOUNT > 0 ROLLBACK; 
        THROW;
    END CATCH;
END

【讨论】:

以上是关于SQL Server 中的事务和@@Error 函数的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server——事务

sql server中的事务是啥意思

如何处理SQL Server事务复制中的大事务操作

Microsoft SQL Server中的事务与并发详解

Sql Server 中的大事务,有啥问题吗?

SQL Server中的事务与锁