2010-01-24 63 views
17

您是否認爲有更好的方法來在t-sql中編寫事務?是否有更好的方法來提高使用此事務的應用程序的可維護性和性能?在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 

回答

33

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

這樣,你不要不斷重複的代碼檢查相同的塊@ @錯誤。如果你想知道發生了什麼錯誤,在BEGIN CATCH塊就可以得到各種信息位的:

  • ERROR_NUMBER()返回錯誤的數量。
  • ERROR_SEVERITY()返回嚴重性。
  • ERROR_STATE()返回錯誤狀態編號。
  • ERROR_PROCEDURE()返回發生錯誤的存儲過程的名稱或觸發器 。
  • ERROR_LINE()返回導致 錯誤的例程內的行號。
  • ERROR_MESSAGE()返回錯誤消息的完整文本。文本 包括爲任何 可替換參數(如 長度,對象名稱或時間)提供的值。
+13

我會把COMMIT TRANSACTION放入BEGIN TRY .... END TRY塊 - 不是在整個語句之後。這不是更簡單,更準確嗎? – 2010-01-24 15:42:42

+0

爲什麼不在'BEGIN TRY'之後放置'BEGIN TRANSACTION'呢? – 2016-06-16 15:23:36

+0

這是6年前......記不起我在想什麼;)但是,我會把交易放在BEGIN TRY裏面。我已經更新了答案。 – AdaTheDev 2016-06-16 15:48:15

5

如果您有SQL Server 2000或之前,則是 - 檢查@@ERROR值基本上是你能做的一切。

隨着SQL Server 2005中,微軟引入了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 
2

如果您使用的是SQL 2005或更高,你應該考慮TRY CATCH方法

2

你可以把它全部封裝在try catch中,然後你只需要在一個地方編寫回滾代碼。有關更多詳情,請參閱this

10

很長一段時間以來,我一直主張使用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交易處理基本上無用,我建議避免。

+0

第三個缺點是過度的複製和粘貼......真的希望有一種方法可以避免所有的重複。 – Aaronaught 2010-01-24 18:28:57

+1

@Aaronaught:不幸的是,這是關於Transact-SQL的藝術狀態。代碼重用和簡潔性不僅僅是一種語言。通過工具生成代碼(例如XSTL + XML)通過消除編寫T-SQL的重複性和容易出錯的特性來緩解該問題。 – 2010-01-25 01:51:06