【问题标题】:"The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION" I keep getting this error when I try to execute my stored procedure,Help me“ROLLBACK TRANSACTION 请求没有相应的 BEGIN TRANSACTION”当我尝试执行我的存储过程时,我不断收到此错误,请帮助我
【发布时间】:2021-09-02 22:13:24
【问题描述】:
ALTER PROCEDURE Add_Edit_Courses_new
    @CourseCode          VARCHAR,
    ... other params ...
AS
    BEGIN TRY
        DECLARE @ErrorCode INT =0, @ErrorMessage VARCHAR(25) = 'Action failed'

        IF @TaskType > 2
            BEGIN
                RAISERROR('Wrong action key',16,1)
            END
        ELSE 
            BEGIN TRANSACTION 
            BEGIN
                DECLARE @message VARCHAR(MAX)
                IF @TaskType = 1
                BEGIN
                    INSERT INTO Courses(...) VALUES(@CourseCode,...)
                    SET @message = 'Added Successfully'
                END
                ELSE IF @TaskType = 2
                BEGIN
                    UPDATE Courses SET CourseCode=@CourseCode,...;
                    SET @message = 'Modified Successfully'
                END                                                 
            END
            COMMIT TRANSACTION 
    END TRY
    BEGIN CATCH
      ROLLBACK TRANSACTION 
      SELECT ERROR_NUMBER() AS ErrorNumber, ...
    END CATCH

我编写了这个存储过程来插入和更新,我在使用 try 和 catch 时使用 (1 & 2) 来区分任务,但是每次我尝试执行这个存储过程时,我都会遇到这个错误,请问你能帮助我解决我的错误,我只是第一次学习这个原则。

【问题讨论】:

标签: sql-server tsql error-handling


【解决方案1】:

为什么是BEGIN TRANSACTION 之前 BEGIN?我觉得 BEGIN TRANSACTION/COMMIT TRANSACTION 应该是 inside ELSE 有条件的。去除一些噪音:

    IF @TaskType > 2
    BEGIN
        RAISERROR('Wrong action key',16,1);
    END
    ELSE 
    BEGIN -- moved this here

        BEGIN TRANSACTION;
        -- BEGIN -- removed this

          DECLARE @message varchar(max);

          IF @TaskType = 1
          BEGIN
            INSERT INTO Courses(...
            SET @message = 'Added Successfully';
          END

          IF @TaskType = 2 -- don't really need ELSE there
          BEGIN
            UPDATE Courses SET ...
            SET @message = 'Modified Successfully';
          END
                                                
        -- END -- removed this 
        COMMIT TRANSACTION;
        SELECT @message;
    END -- moved this here

在你的捕获中你只是盲目地说:

ROLLBACK TRANSACTION;

这应该更新为:

IF @@TRANCOUNT > 0
BEGIN
  ROLLBACK TRANSACTION;
END

请注意,如果您有一个条件,其中多个语句未正确包装在 BEGIN / END 中,它们将不会像您想象的那样执行。考虑:

IF 1 = 0
  PRINT 'foo';
  PRINT 'bar';

无论条件结果如何,每次都会得到bar 输出。

你有类似的东西:

IF 1 = 1
  -- do stuff
ELSE
  BEGIN TRANSACTION;
  BEGIN 
    -- do stuff
  END
  COMMIT TRANSACTION;

在这种情况下,-- do stuff 和提交每次都发生,即使开始事务没有发生,因为 BEGIN/END 包装器(以及它后面的任何东西)与 ELSE 无关.

如果您是该主题的新手,可能有点枯燥和罗嗦,但 Erland Sommarskog 在这里有一个非常全面的错误处理系列,可能值得收藏:

【讨论】:

  • 你有没有看到过样板文件TRY/CATCH/ROLLBACK/SELECT ERROR... 的原因?我不明白为什么人们使用它。我们应该坚持使用SET XACT_ABORT ON;,它保证无论如何都会回滚,并且不会吞下异常
  • @Charlieface 我更喜欢 TRY/CATCH,因为有些异常比其他异常更重要,有时您可以安全地忽略错误情况,有时您想做其他事情而不是仅仅中止(例如记录错误或在某处设置某些状态)。
  • 但是SELECT ERROR... 只是邪恶的。 THROW; 会更好,因为它会重新抛出原始异常(可能不止一个)。 XACT_ABORT 无论如何都应该开启
  • @Charlieface 我并不赞同或干涉这个特定的 OP 如何处理捕获,这似乎与他们的条件逻辑如何流动不同的讨论。
【解决方案2】:

Imo 此代码存在一些重大问题。首先,如果打算使事务成为原子事务,则应指定SET XACT_ABORT ON。根据文档RAISERROR 不尊重SET XACT_ABORT ON,因此可以将其转换为使用THROW。其次,如果代码中的ELSE 块被命中,那么COMMIT 将始终被命中(无论事务的可提交状态发生什么)。

另外,代码在代码之前执行ROLLBACK

 SELECT ERROR_NUMBER() AS ErrorNumber, ...

ROLLBACK 清除错误消息并将状态恢复正常。在ROLLBACK 发生之前捕获错误元数据SELECT

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-30
    • 1970-01-01
    • 2021-01-02
    • 2021-06-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多