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

时间:2010-01-24 15:37:25

标签: sql-server tsql

您认为在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

6 个答案:

答案 0 :(得分:35)

如果您使用的是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;

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

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

答案 1 :(得分:12)

很长一段时间以来,我一直在倡导使用TRY/CATCH and nested transactions in stored procedures

与@@ 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

这种方法的缺点是:

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

还要谨慎使用SET XACT_ABORT ON。此设置将导致批处理在任何错误时中止事务。这引起任何TRY / CATCH交易处理基本无用,我建议避免使用。

答案 2 :(得分:7)

如果你有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

答案 3 :(得分:4)

不久前问道。 My answer带有TRY / CATCH模板

答案 4 :(得分:2)

如果您使用的是sql 2005或更高版本,则应考虑使用TRY CATCH方法

答案 5 :(得分:2)

您可以将它全部包装在try catch中,然后您只需要在一个位置编写回滚代码。有关详细信息,请参阅this