【问题标题】:SQL Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statementsEXECUTE 之后的 SQL 事务计数指示 BEGIN 和 COMMIT 语句的数量不匹配
【发布时间】: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 &lt;&gt;0。这是合乎逻辑的废话,永远也达不到。任何异常都将被捕获到一个 catch 块中(如果它存在的话)。不知道为什么您需要捕获错误只是为了重新引发错误并设置输出参数 - 但也许您有非常非典型的要求。一般来说,你不应该试图通过使用你自己的来破坏异常逻辑。
  • 你真的应该使用THROW,正如RAISERROR 上的文档告诉你的那样。
  • 而且您似乎尝试“手动”执行自然键(姓名、出生日期)。不要 - 应该使用唯一的约束来强制执行。此外,您应该使用 THROW 而不是 RAISERROR。最后 - 阅读有关XACT_ABORT 的文档。搜索 @@IDENTITY 以及为什么这也可能是个坏主意。
  • 最后,Erland 对error handling 进行了很好的讨论——这并不像人们想象的那样直截了当。收藏他的网站,因为它充满了有用的信息。
  • 您是直接从 SSMS 查询窗口还是其他方法执行 proc?如果在 proc 调用之前打开事务并且 proc 在错误后回滚事务,您将收到该错误。

标签: sql-server stored-procedures error-handling transactions


【解决方案1】:

请试试这个方法:

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
-- settings
SET NOCOUNT ON;
SET XACT_ABORT ON;
-- variables
DECLARE @TempVolunteerID INT = 0, @VolunteerID INT, @VolunteerPersonID INT;
SET @Success = 0;
SET @ErrorStatus = '';

-- controled transaction
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 XACT_STATE() <> 0
   BEGIN
      ROLLBACK;
      SELECT @ErrorStatus = @ErrorStatus + CONVERT(NVARCHAR(50), ERROR_MESSAGE());
   END;
   SET @Success = 0;
   SET @ErrorStatus = @ErrorStatus + '-1001';
   RAISERROR(@ErrorStatus, 15, 1);
END CATCH;       
GO

【讨论】:

    猜你喜欢
    • 2015-12-21
    • 2015-06-23
    • 2012-02-18
    • 2016-06-21
    • 1970-01-01
    • 2018-03-11
    • 2014-03-22
    • 2014-10-25
    • 2016-07-21
    相关资源
    最近更新 更多