2010-01-24 30 views
17

Bạn có nghĩ rằng có cách nào tốt hơn để viết giao dịch trong t-sql không? Có cách tiếp cận nào tốt hơn để cải thiện khả năng bảo trì và hiệu suất của ứng dụng sử dụng giao dịch này không?viết một giao dịch trong t-sql và xử lý lỗi

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

Trả lời

33

Nếu bạn đang sử dụng SQL 2005 hoặc mới hơn, bạn có thể sử dụng các khối TRY...CATCH, như thế này:

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; 

Bằng cách này, bạn không giữ lặp đi lặp lại các khối tương tự kiểm tra mã @ @LỖI. Nếu bạn muốn biết những gì xảy ra lỗi, trong khối catch BEGIN bạn có thể nhận bit khác nhau của thông tin:

  • ERROR_NUMBER() trả về số lỗi.
  • ERROR_SEVERITY() trả về mức độ nghiêm trọng.
  • ERROR_STATE() trả về số trạng thái lỗi.
  • ERROR_PROCEDURE() trả về tên của thủ tục được lưu trữ hoặc kích hoạt nơi xảy ra lỗi.
  • ERROR_LINE() trả về số dòng bên trong quy trình gây ra lỗi .
  • ERROR_MESSAGE() trả về văn bản đầy đủ của thông báo lỗi. Văn bản bao gồm các giá trị được cung cấp cho bất kỳ thông số thay thế nào, chẳng hạn như độ dài, tên đối tượng hoặc thời gian.
+13

Tôi sẽ đặt GIAO DỊCH COMMIT vào khối BEGIN TRY .... END TRY - không phải sau toàn bộ câu lệnh. Nó sẽ không dễ dàng hơn và chính xác hơn? –

+0

tại sao không đặt 'BEGIN TRANSACTION' sau' BEGIN TRY'? –

+0

Điều này đã được 6 năm trước .... không thể nhớ khá những gì tôi đã suy nghĩ;) Nhưng có, tôi sẽ đặt giao dịch bên trong BEGIN TRY. Tôi đã cập nhật câu trả lời. – AdaTheDev

5

Nếu bạn có SQL Server 2000 hoặc trước đó, thì hãy kiểm tra giá trị @@ERROR về cơ bản là tất cả những gì bạn có thể làm.

Với SQL Server 2005, Microsoft đã giới thiệu cấu trúc try ... catch mà làm cho nó dễ dàng hơn nhiều:

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

Nếu bạn đang sử dụng sql 2005 hoặc cao hơn bạn nên xem xét phương pháp TRY CATCH

2

Bạn có thể quấn nó tất cả trong một thử bắt, và sau đó bạn chỉ cần mã rollback ở một nơi. Xem this để biết thêm chi tiết.

4

Đã hỏi cách đây không lâu. My answer với mẫu TRY/CATCH

10

Trong một thời gian dài bây giờ tôi đã ủng hộ việc sử dụng TRY/CATCH and nested transactions in stored procedures.

Mẫu này cung cấp cho bạn không chỉ việc xử lý lỗi đơn giản của khối TRY/CATCH so với kiểm tra @@ ERROR, mà còn cung cấp cho mọi yêu cầu lồng nhau ngữ nghĩa cho lời gọi thủ tục.

Nếu thủ tục được gọi trên ngữ cảnh giao dịch thì thủ tục chỉ quay lại thay đổi riêng của mình và để người gọi quyết định có quay lại giao dịch nhúng hay không.

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 

Các lưng bốc thăm của phương pháp này là:

  • không làm việc với các giao dịch phân tán. Bởi vì các điểm lưu lượng giao dịch không tương thích với các giao dịch phân tán, bạn không thể sử dụng mẫu này khi giao dịch được phân phối được yêu cầu. Các giao dịch phân phối IMHO là ác và không bao giờ được sử dụng.
  • làm thay đổi lỗi ban đầu. Vấn đề này là cố hữu trong khối TRY/CATCH và không có gì bạn có thể làm về nó. Một ứng dụng được chuẩn bị để đối phó với các mã lỗi SQL Server ban đầu (như 1202, 1205, 2627 vv) sẽ phải được thay đổi để xử lý các mã lỗi trong phạm vi trên 50000 nêu trên bằng mã Transact-SQL sử dụng TRY/CATCH .

Cũng là một lời cảnh báo về việc sử dụng SET XACT_ABORT ON. Cài đặt này sẽ khiến một lô hủy bỏ giao dịch ở bất kỳ lỗi nào. Điều đó làm tăng bất kỳ xử lý giao dịch TRY/CATCH nào về cơ bản vô ích và tôi khuyên bạn nên tránh.

+0

Hạn chế thứ 3 sẽ là bản sao và dán quá nhiều ... thực sự mong muốn có một cách để tránh tất cả sự lặp lại. – Aaronaught

+1

@Aaronaught: Thật không may đó là số phận của nghệ thuật liên quan đến Transact-SQL. Chỉ là không phải là một ngôn ngữ thân thiện để tái sử dụng mã và ngắn gọn. Việc tạo mã thông qua các công cụ (ví dụ: XSTL + XML) đi một chặng đường dài để làm giảm bớt vấn đề đó bằng cách loại bỏ tính chất lặp lại và dễ bị lỗi của việc viết T-SQL. –

Các vấn đề liên quan