在t-sql中编写事务和错误处理

Cos*_*sta 21 t-sql sql-server

您认为在t-sql中编写事务有更好的方法吗?是否有更好的方法可以提高使用此事务的应用程序的可维护性和性能?

-- Description: Insert email Receiver under specified subject
-- =============================================
ALTER PROCEDURE [Contact].[Receiver_stpInsert]
    @First_Name nvarchar(30),
    @Last_Name nvarchar(30),
    @Email varchar(60),
    @Subject_Id int
AS
BEGIN   
    SET NOCOUNT ON;

    DECLARE @error_num int;


    BEGIN TRANSACTION 

    INSERT INTO Contact.Receiver(First_Name, Last_Name, Email) VALUES(@First_Name, @Last_Name, @Email); 

    SET @error_num = @@ERROR;
    IF (@error_num <> 0)
        BEGIN
            ROLLBACK;
            RETURN;
        END

    DECLARE @rec_record_id int;
    SET @rec_record_id = (SELECT Record_Id FROM Contact.Receiver WHERE Email = @Email);

    SET @error_num = @@ERROR;
    IF (@error_num <> 0)
        BEGIN
            ROLLBACK;
            RETURN;
        END

    INSERT INTO Contact.Receiver_Subject(Receiver_Id, Subject_Id) VALUES(@rec_record_id, @Subject_Id);

    SET @error_num = @@ERROR;
    IF (@error_num <> 0)
        BEGIN
            ROLLBACK;
            RETURN;
        END

    SET @error_num = @@ERROR;
    IF (@error_num <> 0)
        BEGIN
            ROLLBACK;
            RETURN;
        END
    ELSE
        BEGIN   
            Commit;

        END

END
Run Code Online (Sandbox Code Playgroud)

Ada*_*Dev 37

如果您使用的是SQL 2005或更高版本,则可以使用TRY ... CATCH块,如下所示:

BEGIN TRY
    BEGIN TRANSACTION;

    INSERT INTO Contact.Receiver(First_Name, Last_Name, Email) VALUES (@First_Name, @Last_Name, @Email); 
    ... other inserts etc 
    ...
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0
        ROLLBACK TRANSACTION;
END CATCH;
Run Code Online (Sandbox Code Playgroud)

这样,您就不会重复检查@@ ERROR的相同代码块.如果您想知道发生了什么错误,可以在BEGIN CATCH块中获取各种信息:

  • ERROR_NUMBER()返回错误的编号.
  • ERROR_SEVERITY()返回严重性.
  • ERROR_STATE()返回错误状态编号.
  • ERROR_PROCEDURE()返回发生错误的存储过程或触发器的名称.
  • ERROR_LINE()返回导致错误的例程内的行号.
  • ERROR_MESSAGE()返回错误消息的完整文本.该文本包括为任何可替换参数提供的值,例如长度,对象名称或时间.

  • 我会把COMMIT TRANSACTION放到BEGIN TRY .... END TRY块中 - 而不是在整个语句之后.这不是更简单,更准确吗? (13认同)

Rem*_*anu 12

很长一段时间以来,我一直在提倡在存储过程中使用TRY/CATCH和嵌套事务.

与@@ ERROR检查相比,此模式不仅为您提供了TRY/CATCH块的简化错误处理,而且还为过程调用提供了全有或全无嵌套语义.

如果在事务的上下文中调用该过程,则该过程回滚其自己的更改,并使调用者决定是回滚嵌入事务还是尝试备用错误路径.

create procedure [usp_my_procedure_name]
as
begin
    set nocount on;
    declare @trancount int;
    set @trancount = @@trancount;
    begin try
        if @trancount = 0
            begin transaction
        else
            save transaction usp_my_procedure_name;

        -- Do the actual work here

lbexit:
        if @trancount = 0   
            commit;
    end try
    begin catch
        declare @error int, @message varchar(4000), @xstate int;
        select @error = ERROR_NUMBER(), @message = ERROR_MESSAGE(), @xstate = XACT_STATE();
        if @xstate = -1
            rollback;
        if @xstate = 1 and @trancount = 0
            rollback
        if @xstate = 1 and @trancount > 0
            rollback transaction usp_my_procedure_name;

        raiserror ('usp_my_procedure_name: %d: %s', 16, 1, @error, @message) ;
        return;
    end catch   
end
Run Code Online (Sandbox Code Playgroud)

这种方法的缺点是:

  • 它不适用于分布式事务.由于事务保存点与分布式事务不兼容,因此在需要分布式事务时不能使用此模式.恕我直言分发的交易是邪恶的,无论如何都不应该使用.
  • 改变了原始错误.这个问题是TRY/CATCH块中固有的,你无能为力.必须更改准备处理原始SQL Server错误代码(如1202,1205,2627等)的应用程序,以处理由使用TRY/CATCH的Transact-SQL代码引发的上述50000范围内的错误代码.

关于SET XACT_ABORT ON的使用也要谨慎.此设置将导致批处理在任何错误时中止事务.这引发任何TRY/CATCH交易处理基本没用,我建议避免.


mar*_*c_s 8

如果你有SQL Server 2000或之前,那么是 - 检查@@ERROR值基本上是你所能做的.

在SQL Server 2005中,Microsoft引入了TRY ... CATCH结构,使其更容易:

BEGIN TRY
  ......
  -- your T-SQL code here
  ......
END TRY
BEGIN CATCH
   SELECT 
      ERROR_NUMBER() AS ErrorNumber,
      ERROR_SEVERITY() AS ErrorSeverity,
      ERROR_STATE() AS ErrorState,
      ERROR_PROCEDURE() AS ErrorProcedure,
      ERROR_LINE() AS ErrorLine,
      ERROR_MESSAGE() AS ErrorMessage

    -- do other steps, if you want
END CATCH
Run Code Online (Sandbox Code Playgroud)