SQL Server - giao dịch quay trở lại lỗi?


192

Chúng tôi có ứng dụng khách đang chạy một số SQL trên SQL Server 2005, chẳng hạn như sau:

BEGIN TRAN;
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
COMMIT TRAN;

Nó được gửi bởi một lệnh chuỗi dài.

Nếu một trong các phần chèn không thành công hoặc bất kỳ phần nào của lệnh không thành công, SQL Server có khôi phục giao dịch không? Nếu nó không quay trở lại, tôi có phải gửi lệnh thứ hai để quay lại không?

Tôi có thể cung cấp thông tin cụ thể về api và ngôn ngữ tôi đang sử dụng, nhưng tôi nghĩ SQL Server sẽ phản hồi tương tự đối với bất kỳ ngôn ngữ nào.


Câu trả lời:


204

Bạn có thể đặt set xact_abort ontrước giao dịch của mình để đảm bảo sql tự động quay lại trong trường hợp có lỗi.


1
Điều này có hoạt động trên MS SQL 2K trở lên không? Đây có vẻ là giải pháp đơn giản nhất.
jonathanpeppers

1
Nó xuất hiện trong các tài liệu cho năm 2000, 2005 và 2008 vì vậy tôi cho là có. Chúng tôi đang sử dụng nó trong năm 2008

8
Tôi có cần tắt nó đi hay là mỗi phiên?
Marc

5
@Marc phạm vi của xact_abortlà ở cấp độ kết nối.
Keith

2
@AlexMcMillan Câu lệnh DROP PROCEDURE sửa đổi cấu trúc cơ sở dữ liệu, không giống như INSERT, chỉ hoạt động với dữ liệu. Vì vậy, nó không thể được bọc trong một giao dịch. Tôi đơn giản hóa, nhưng về cơ bản là như vậy.
eksortso

195

Bạn đúng ở chỗ toàn bộ giao dịch sẽ được khôi phục. Bạn nên đưa ra lệnh để cuộn lại.

Bạn có thể gói cái này trong một TRY CATCHkhối như sau

BEGIN TRY
    BEGIN TRANSACTION

        INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
        INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
        INSERT INTO myTable (myColumns ...) VALUES (myValues ...);

    COMMIT TRAN -- Transaction Success!
END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0
        ROLLBACK TRAN --RollBack in case of Error

    -- you can Raise ERROR with RAISEERROR() Statement including the details of the exception
    RAISERROR(ERROR_MESSAGE(), ERROR_SEVERITY(), 1)
END CATCH

2
Tôi thích giải pháp của DyingCactus hơn, đây là 1 dòng mã để thay đổi. Nếu bạn vì lý do nào đó tốt hơn (hoặc đáng tin cậy hơn) cho tôi biết.
jonathanpeppers

13
Việc bắt thử cung cấp cho bạn khả năng nắm bắt (và có thể sửa) lỗi và đưa ra thông báo lỗi tùy chỉnh nếu được yêu cầu.
Raj Thêm

10
"Chụp và ghi nhật ký" thường xuyên hơn "chụp và sửa", tôi nghĩ vậy.
quillbreaker

24
Cú pháp của RAISERROR không chính xác ít nhất là trong SQL Server 2008R2 trở lên. Xem msdn.microsoft.com/en-us/l Library / ms178592.aspx để biết cú pháp chính xác.
Eric J.

2
@BornToCode Để đảm bảo giao dịch tồn tại .. Hãy nói rằng bạn đã khôi phục giao dịch của mình trong điều kiện nhất định (trong try), nhưng mã bị lỗi sau đó. Không còn giao dịch nữa, nhưng bạn vẫn sẽ tham gia catch.
Gabriel GM

42

Đây là mã nhận thông báo lỗi hoạt động với MSSQL Server 2016:

BEGIN TRY
    BEGIN TRANSACTION 
        -- Do your stuff that might fail here
    COMMIT
END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0
        ROLLBACK TRAN

        DECLARE @ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE()
        DECLARE @ErrorSeverity INT = ERROR_SEVERITY()
        DECLARE @ErrorState INT = ERROR_STATE()

    -- Use RAISERROR inside the CATCH block to return error  
    -- information about the original error that caused  
    -- execution to jump to the CATCH block.  
    RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState);
END CATCH

1
Tôi đã phải sử dụng DECLARE @Var TYPE; SET @Var = ERROR;để tăng lỗi trong máy chủ sql 2005. Nếu không, đoạn mã trên để tăng lỗi cũng hoạt động với các DB cũ hơn. Cố gắng gán giá trị mặc định cho biến cục bộ là điều gây ra sự cố.
jtlindsey

Bạn có thể sử dụng một TÊN đơn giản; thay vì khai báo RAISERROR và ERROR_ *.
Rodzmkii

21

Từ bài viết MDSN, Kiểm soát giao dịch (Cơ sở dữ liệu) .

Nếu xảy ra lỗi câu lệnh thời gian chạy (chẳng hạn như vi phạm ràng buộc), thì hành vi mặc định trong Công cụ cơ sở dữ liệu là chỉ quay lại câu lệnh tạo ra lỗi. Bạn có thể thay đổi hành vi này bằng cách sử dụng câu lệnh SET XACT_ABORT. Sau khi SET XACT_ABORT ON được thực thi, bất kỳ lỗi câu lệnh thời gian chạy nào cũng gây ra sự phục hồi tự động của giao dịch hiện tại. Lỗi biên dịch, chẳng hạn như lỗi cú pháp, không bị ảnh hưởng bởi SET XACT_ABORT. Để biết thêm thông tin, hãy xem SET XACT_ABORT (Transact-SQL).

Trong trường hợp của bạn, nó sẽ phục hồi giao dịch hoàn chỉnh khi bất kỳ thao tác chèn nào không thành công.


3
Chúng ta cần gì để xử lý lỗi cú pháp? hoặc biên dịch lỗi? nếu bất kỳ ai trong số họ xảy ra, toàn bộ giao dịch sẽ được khôi phục
MonsterMMORPG

Nắm bắt lỗi biên dịch / cú pháp là những gì các dự án SSDT dành cho. :-)
Joe the Coder

10

Nếu một trong các phần chèn không thành công hoặc bất kỳ phần nào của lệnh bị lỗi, máy chủ SQL có khôi phục giao dịch không?

Không nó không.

Nếu nó không quay trở lại, tôi có phải gửi lệnh thứ hai để quay lại không?

Chắc chắn, bạn nên phát hành ROLLBACKthay vì COMMIT.

Nếu bạn muốn quyết định nên cam kết hoặc hoàn trả giao dịch, bạn nên xóa COMMITcâu ra khỏi câu lệnh, kiểm tra kết quả của các lần chèn và sau đó đưa ra COMMIThoặc ROLLBACKtùy thuộc vào kết quả kiểm tra.


Vì vậy, nếu tôi gặp lỗi, hãy nói "Xung đột khóa chính" Tôi cần gửi cuộc gọi thứ hai để quay lại? Tôi đoán điều đó có ý nghĩa. Điều gì xảy ra nếu có lỗi liên quan đến mạng như kết nối bị đứt trong câu lệnh SQL chạy rất dài?
jonathanpeppers

2
Khi hết thời gian kết nối, giao thức mạng cơ bản (ví dụ Named Pipeshoặc TCP) ngắt kết nối. Khi một kết nối bị hỏng, SQL Serverdừng tất cả các lệnh hiện đang chạy và khôi phục giao dịch.
Quassnoi

1
Vì vậy, giải pháp của DyingCactus có vẻ như đã khắc phục vấn đề của tôi, cảm ơn vì sự giúp đỡ.
jonathanpeppers

Nếu bạn cần hủy bỏ bất kỳ lỗi nào , thì có, đây là lựa chọn tốt nhất.
Quassnoi
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.