【发布时间】:2021-05-23 22:31:05
【问题描述】:
我正在处理一个企业数据库管理课程项目,似乎无法让我的错误处理在事务中正确执行。我的教练建议我看一下错误“Msg 266, Level 16, State 2, Procedure pInsertVolunteer, Line 28 EXECUTE 之后的事务计数表明 BEGIN 和 COMMIT 语句的数量不匹配。 " 但我还没有找到任何适用于此事务的解决方案。
CREATE PROCEDURE [dbo].[pInsertVolunteer]
@LastName varchar(255),
@FirstName varchar(255),
@DateOfBirth date,
@Note varchar(MAX),
@ModifiedBy nvarchar(50),
@Success bit Output,
@ErrorStatus Nvarchar(50) Output
AS
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN
DECLARE
@TempVolunteerID int,
@VolunteerID int,
@VolunteerPersonID int;
SET @TempVolunteerID = 0;
SET @Success = 0;
SET @ErrorStatus = '';
END
BEGIN TRY
BEGIN TRANSACTION;
SELECT @TempVolunteerID = VolunteerID
FROM Volunteer.VolunteerInfo
WHERE FirstName = @FirstName AND LastName = @LastName AND DateOfBirth = @DateOfBirth
If @TempVolunteerID <>0
BEGIN
SELECT @ErrorStatus = 'Person Already Exists';
SET @Success = 0;
RAISERROR (@ErrorStatus, 16,1);
END
IF @@ERROR <>0
BEGIN
SELECT @ErrorStatus = CONVERT (nvarchar(50),@@ERROR) + '-1000';
SET @Success = 0;
RAISERROR(@ErrorStatus, 16,1);
END
INSERT INTO Volunteer.VolunteerInfo (LastName, FirstName, DateOfBirth)
values (@LastName, @FirstName, @DateofBirth)
SET @Success = 1;
SET @ErrorStatus = 0;
SELECT @VolunteerPersonID = VolunteerID
FROM Volunteer.VolunteerInfo
If @VolunteerPersonID = 0 OR @VolunteerPersonID IS NULL
BEGIN
SET @Success =0;
SET @ErrorStatus = '8008: PersonID must be valid';
RAISERROR(@ErrorStatus,16,1);
END
Insert into Volunteer.VolunteerNotes (Note,ModifiedDate, ModifiedBy)
Values (@Note,SYSDATETIME(), @ModifiedBy)
SET @VolunteerPersonID = @@IDENTITY;
SET @Success = 1;
SET @ErrorStatus = 0;
SET NOCOUNT OFF;
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
BEGIN
SELECT @ErrorStatus = @ErrorStatus + CONVERT (nvarchar(50), @@ERROR);
END
SET @Success = 0;
SET @ErrorStatus = @ErrorStatus + '-1001';
RAISERROR(@ErrorStatus, 15,1)
ROLLBACK TRANSACTION;
END CATCH
GO
任何帮助将不胜感激。
谢谢!
【问题讨论】:
-
格式化您的代码并仔细使用缩进将帮助其他人极大地理解它。但是您似乎不了解 try/catch。在第一个 SELECT 语句之后有
IF @@ERROR <>0。这是合乎逻辑的废话,永远也达不到。任何异常都将被捕获到一个 catch 块中(如果它存在的话)。不知道为什么您需要捕获错误只是为了重新引发错误并设置输出参数 - 但也许您有非常非典型的要求。一般来说,你不应该试图通过使用你自己的来破坏异常逻辑。 -
你真的应该使用
THROW,正如RAISERROR上的文档告诉你的那样。 -
而且您似乎尝试“手动”执行自然键(姓名、出生日期)。不要 - 应该使用唯一的约束来强制执行。此外,您应该使用 THROW 而不是 RAISERROR。最后 - 阅读有关XACT_ABORT 的文档。搜索 @@IDENTITY 以及为什么这也可能是个坏主意。
-
最后,Erland 对error handling 进行了很好的讨论——这并不像人们想象的那样直截了当。收藏他的网站,因为它充满了有用的信息。
-
您是直接从 SSMS 查询窗口还是其他方法执行 proc?如果在 proc 调用之前打开事务并且 proc 在错误后回滚事务,您将收到该错误。
标签: sql-server stored-procedures error-handling transactions