"The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION" 我在尝试执行存储过程时不断收到此错误,请帮助我


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

我写了这个存储过程来插入和更新,我使用了(1 &2)在使用try和catch的同时区分任务,但每次我尝试执行这个存储过程时,我总是得到那个错误,请您帮助我哪里错了,我只是第一次学习这个原则。

为什么BEGIN TRANSACTIONBEGIN之前?我觉得BEGIN TRANSACTION/COMMIT TRANSACTION应该在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和commit每次都发生,即使begin事务没有发生,因为BEGIN/END包装器(以及其后的任何东西)没有与ELSE相关联。

如果您不熟悉这个主题,可能会觉得有点枯燥和冗长,但是Erland Sommarskog在这里有一个非常全面的错误处理系列,可能值得收藏:

  • https://www.sommarskog.se/error_handling/Part1.html
  • https://www.sommarskog.se/error_handling/Part2.html
  • https://www.sommarskog.se/error_handling/Part3.html

在我看来,这段代码存在一些主要问题。首先,如果希望事务是原子的,那么应该指定SET XACT_ABORT ON。根据文档RAISERROR不尊重SET XACT_ABORT ON,所以它可以转换为使用THROW。其次,在代码中的ELSE块被击中的情况下,COMMIT将始终被击中(无论事务的可提交状态发生了什么)。

同样,代码执行ROLLBACK之前的代码:

SELECT ERROR_NUMBER() AS ErrorNumber, ...

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

相关内容

最新更新