Số lượng giao dịch sau khi EXECUTE cho biết số lượng câu lệnh BEGIN và COMMIT không khớp. Số lượng trước đây = 1, số lượng hiện tại = 0


94

Tôi có một Insertthủ tục được lưu trữ sẽ cấp dữ liệu đến Table1và lấy Column1giá trị từ đó Table1và gọi thủ tục được lưu trữ thứ hai sẽ cấp dữ liệu cho Table2.

Nhưng khi tôi gọi Thủ tục được lưu trữ thứ hai là:

Exec USPStoredProcName

Tôi nhận được lỗi sau đây:

Số lượng giao dịch sau khi EXECUTE cho biết số lượng câu lệnh BEGIN và COMMIT không khớp. Số lượng trước đây = 1, số lượng hiện tại = 0.

Tôi đã đọc câu trả lời trong những câu hỏi khác như vậy và không thể tìm thấy nơi chính xác số lượng cam kết đang bị lộn xộn.


Bạn có bất kỳ khối TRY / CATCH nào trong quy trình của mình không?
Remus Rusanu

Có, tôi có khối TRY / CATCH
Vignesh Kumar A

Câu trả lời:


109

Nếu bạn có khối TRY / CATCH thì nguyên nhân có thể là bạn đang bắt một ngoại lệ hủy bỏ giao dịch và tiếp tục. Trong khối CATCH, bạn phải luôn kiểm tra XACT_STATE()và xử lý các giao dịch bị hủy bỏ và không thể chấp nhận (bị hủy bỏ) thích hợp. Nếu người gọi của bạn bắt đầu một giao dịch và lịch trình xảy ra, chẳng hạn như bế tắc (đã hủy bỏ giao dịch), làm thế nào người gọi sẽ thông báo với người gọi rằng giao dịch đã bị hủy bỏ và nó sẽ không tiếp tục với 'hoạt động kinh doanh như bình thường'? Cách khả thi duy nhất là nâng lại một ngoại lệ, buộc người gọi phải xử lý tình huống. Nếu bạn âm thầm nuốt một giao dịch đã hủy bỏ và người gọi tiếp tục giả sử vẫn còn trong giao dịch ban đầu, chỉ có thể đảm bảo tình trạng lộn xộn (và lỗi bạn nhận được là cách động cơ cố gắng bảo vệ chính nó).

Tôi khuyên bạn nên xem qua Xử lý ngoại lệ và các giao dịch lồng nhau cho thấy một mẫu có thể được sử dụng với các giao dịch và ngoại lệ lồng nhau:

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) ;
    end catch
end
go

3
Cảm ơn bạn đã giúp đỡ. Bằng cách sử dụng RAISERROR Tôi đã tìm thấy của problem.It về cố gắng để chèn giá trị NULL đến lĩnh vực NOT NULL
Vignesh Kumar Một

Nhưng xác thực kiểm tra ràng buộc sẽ không hủy bỏ giao dịch. Bạn có đang quay trở lại một cách rõ ràng hay bạn sử dụng xact_abort on?
Remus Rusanu

Tôi rõ ràng đang quay trở lại
Vignesh Kumar A

2
Tôi đã thử mẫu này nhưng vẫn không hoạt động - khi tôi có giao dịch bên ngoài, mẫu này sẽ tạo điểm lưu và trong trường hợp xảy ra lỗi nghiêm trọng (giao dịch không thể thay đổi) sẽ khôi phục giao dịch bên ngoài - điều này vẫn gây ra @@ trancount = 1 trước khi nhập thủ tục và @@ trancount = 0 khi thoát khỏi nó
chim sẻ

3
Tôi nghĩ rằng bit này trong CATCH là sai: if @xstate = -1 rollback; Nhìn vào ví dụ MSDN này , chúng ta không nên khôi phục toàn bộ giao dịch trừ khi không có giao dịch bên ngoài (nghĩa là, trừ khi chúng ta đã làm begin tran). Tôi nghĩ thủ tục chỉ nên thực hiện rollbackkhi chúng tôi bắt đầu giao dịch, điều này sẽ khắc phục được sự cố của @ sparrow.
Nick

59

Tôi cũng có vấn đề này. Đối với tôi, lý do là tôi đã làm

return
commit

thay vì

commit
return   

trong một thủ tục được lưu trữ.


4
@seguso - điều này rất hữu ích. Cảm ơn bạn đã chia sẻ. Đôi khi một cái gì đó rất đơn giản nằm bên dưới lớp bụi. Xảy ra những điều tốt đẹp nhất của chúng.
Leo Gurdian

Đây là vấn đề đối với tôi, nhưng ít rõ ràng hơn bởi vì chúng tôi đã gói một số lệnh gọi của mầm trong một giao dịch lớn thông qua lớp truy cập dữ liệu của chúng tôi - vì vậy chỉ nhìn vào mầm, bạn không thể biết có giao dịch nào cả. Nếu bạn gặp sự cố này, hãy đảm bảo rằng không có thứ gì đó bên ngoài bản thân mầm đang tạo giao dịch. Nếu có thì bạn có thể hoàn toàn không sử dụng được câu lệnh trả về trong chương trình mầm non.
EF0

Đây là tôi, tôi đã có một giao dịch và được trở về trước khi giao dịch cam kết của tôi trong một tuyên bố else if /
Kevin

18

Điều này thường xảy ra khi giao dịch được bắt đầu và nó không được cam kết hoặc nó không được khôi phục.

Trong trường hợp lỗi xảy ra trong thủ tục được lưu trữ của bạn, điều này có thể khóa các bảng cơ sở dữ liệu vì giao dịch không được hoàn thành do một số lỗi thời gian chạy trong trường hợp không xử lý ngoại lệ. Bạn có thể sử dụng xử lý ngoại lệ như bên dưới. ĐẶT XACT_ABORT

SET XACT_ABORT ON
SET NoCount ON
Begin Try 
     BEGIN TRANSACTION 
        //Insert ,update queries    
     COMMIT
End Try 
Begin Catch 
     ROLLBACK
End Catch

Nguồn


Nếu đây là trường hợp, câu hỏi / câu trả lời được trích dẫn có thể có nghĩa là câu hỏi / câu trả lời này phải được đánh dấu là trùng lặp và bị đóng
Mark Schultheiss

10

Hãy lưu ý rằng nếu bạn sử dụng các giao dịch lồng nhau, hoạt động ROLLBACK sẽ khôi phục tất cả các giao dịch lồng nhau bao gồm cả giao dịch ngoài cùng.

Điều này có thể, với việc sử dụng kết hợp với TRY / CATCH, dẫn đến lỗi bạn đã mô tả. Xem thêm tại đây .


5

Điều này cũng có thể xảy ra nếu thủ tục được lưu trữ của bạn gặp lỗi biên dịch sau khi mở giao dịch (ví dụ: không tìm thấy bảng, tên cột không hợp lệ).

Tôi thấy tôi phải sử dụng 2 thủ tục được lưu trữ, một "worker" và một wrapper với try / catch cả hai với logic tương tự như được phác thảo bởi Remus Rusanu. Công cụ bắt được sử dụng để xử lý các lỗi "bình thường" và bắt trình bao bọc để xử lý các lỗi biên dịch thất bại.

https://msdn.microsoft.com/en-us/library/ms175976.aspx

Lỗi không bị ảnh hưởng bởi TRY… CATCH Cấu trúc

Các loại lỗi sau không được khối CATCH xử lý khi chúng xảy ra ở cùng mức thực thi với cấu trúc TRY… CATCH:

  • Biên dịch lỗi, chẳng hạn như lỗi cú pháp , ngăn không cho chạy một loạt.
  • Các lỗi xảy ra trong quá trình biên dịch lại cấp câu lệnh, chẳng hạn như lỗi phân giải tên đối tượng xảy ra sau khi biên dịch vì phân giải tên bị trì hoãn.

Hy vọng rằng điều này sẽ giúp người khác tiết kiệm vài giờ gỡ lỗi ...


1
Cảm ơn Justin. Quan sát tốt đẹp. Trong trường hợp của tôi, tôi đang thực hiện tổng hợp bên trong bản cập nhật không tạo ra lỗi biên dịch trong quá trình lưu SP nhưng cú pháp thực sự không hợp lệ - "Tổng hợp có thể không xuất hiện trong danh sách tập hợp của một câu lệnh CẬP NHẬT"
kuklei

4

Trong trường hợp của tôi, lỗi được gây ra bởi RETURNbên trong BEGIN TRANSACTION. Vì vậy, tôi đã có một cái gì đó như thế này:

Begin Transaction
 If (@something = 'foo')
 Begin
     --- do some stuff
     Return
 End
commit

và nó cần phải:

Begin Transaction
 If (@something = 'foo')
 Begin
     --- do some stuff
     Rollback Transaction ----- THIS WAS MISSING
     Return
 End
commit

2

Đối với tôi sau khi gỡ lỗi rộng rãi, bản sửa lỗi là một lỗi đơn giản; trong lệnh bắt sau khi khôi phục. Nếu không có nó, thông báo lỗi xấu xí này là kết quả của bạn.

begin catch
    if @@trancount > 0 rollback transaction;
    throw; --allows capture of useful info when an exception happens within the transaction
end catch

2

Tôi đã gặp phải thông báo lỗi tương tự, lỗi của tôi là tôi có dấu chấm phẩy ở cuối dòng GIAO DỊCH CAM KẾT


Đơn giản như thế này. Ngoài ra, trường hợp của tôi cần câu lệnh 'ROLLBACK' trong trường hợp SP không được thực thi đầy đủ. Chỉ để đóng / kết thúc giao dịch.
J Cordero

1

Tôi đã gặp lỗi này một lần sau khi bỏ qua bảng sao kê này khỏi giao dịch của mình.

COMMIT TRANSACTION [MyTransactionName]

1

Theo tôi câu trả lời được chấp nhận trong hầu hết các trường hợp là quá mức cần thiết.

Nguyên nhân lỗi thường do lỗi BEGIN và CAM KẾT không khớp như đã nêu rõ. Điều này có nghĩa là sử dụng:

Begin
  Begin
    -- your query here
  End
commit

thay vì

Begin Transaction
  Begin
    -- your query here
  End
commit

bỏ qua Giao dịch sau khi Bắt đầu gây ra lỗi này!


1

Đảm bảo rằng bạn không có nhiều giao dịch trong cùng một thủ tục / truy vấn mà một hoặc nhiều giao dịch không bị giới hạn.

Trong trường hợp của tôi, tôi vô tình có câu lệnh BEGIN TRAN trong truy vấn


1

Điều này cũng có thể phụ thuộc vào cách bạn đang gọi SP từ mã C # của mình. Nếu SP trả về một số giá trị kiểu bảng thì gọi SP bằng ExecuteStoreQuery và nếu SP không trả về bất kỳ giá trị nào thì gọi SP bằng ExecuteStoreCommand


1

Tránh sử dụng

RETURN

tuyên bố khi bạn đang sử dụng

BEGIN TRY
    ... 
END TRY

BEGIN CATCH
    ...
END CATCH

BEGIN, COMMIT & ROLLBACK

câu lệnh trong thủ tục lưu trữ SQL


0

Nếu bạn đang có một cấu trúc mã như:

SELECT 151
RETURN -151

Sau đó sử dụng:

SELECT 151
ROLLBACK
RETURN -151
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.