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 TRANSACTION
在BEGIN
之前?我觉得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
。