Jumlah transaksi setelah EXECUTE menunjukkan jumlah pernyataan BEGIN dan COMMIT yang tidak sesuai. Hitungan sebelumnya = 1, hitungan saat ini = 0

95

Saya memiliki Insertprosedur tersimpan yang akan memberi makan data ke Table1dan mendapatkan Column1nilai dari Table1dan memanggil prosedur tersimpan kedua yang akan memberi makan Tabel2.

Tetapi ketika saya memanggil prosedur tersimpan kedua sebagai:

Exec USPStoredProcName

Saya mendapatkan kesalahan berikut:

Jumlah transaksi setelah EXECUTE menunjukkan jumlah pernyataan BEGIN dan COMMIT yang tidak sesuai. Hitungan sebelumnya = 1, hitungan saat ini = 0.

Saya telah membaca jawaban dalam pertanyaan lain semacam itu dan saya tidak dapat menemukan di mana tepatnya jumlah commit menjadi kacau.

Vignesh Kumar A
sumber
Apakah Anda memiliki blok TRY / CATCH dalam prosedur Anda?
Remus Rusanu
Ya, saya memiliki blok TRY / CATCH
Vignesh Kumar A

Jawaban:

110

Jika Anda memiliki blok TRY / CATCH maka kemungkinan besar penyebabnya adalah Anda menangkap pengecualian pembatalan transaksi dan melanjutkan. Dalam blok CATCH Anda harus selalu memeriksa XACT_STATE()dan menangani transaksi yang dibatalkan dan tidak dapat dilakukan (ditakdirkan). Jika penelepon Anda memulai transaksi dan calee menemukan, katakanlah, kebuntuan (yang membatalkan transaksi), bagaimana callee akan mengkomunikasikan kepada penelepon bahwa transaksi telah dibatalkan dan tidak boleh dilanjutkan dengan 'bisnis seperti biasa'? Satu-satunya cara yang layak adalah memunculkan kembali pengecualian, memaksa pemanggil untuk menangani situasi tersebut. Jika Anda diam-diam menelan transaksi yang dibatalkan dan penelepon terus menganggap masih dalam transaksi asli, hanya kekacauan yang dapat memastikan (dan kesalahan yang Anda dapatkan adalah cara mesin mencoba melindungi dirinya sendiri).

Saya sarankan Anda membahas penanganan Exception dan transaksi bersarang yang menunjukkan pola yang dapat digunakan dengan transaksi dan pengecualian bersarang:

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
Remus Rusanu
sumber
3
Terima kasih atas bantuan Anda. Dengan menggunakan Raiserror saya telah menemukan masalahnya, yaitu tentang mencoba memasukkan nilai NULL ke bidang NOT NULL
Vignesh Kumar A
Tapi validasi cek kendala tidak akan membatalkan transaksi. Apakah Anda secara eksplisit memutar balik tangkapan, atau apakah Anda menggunakan xact_abort on?
Remus Rusanu
Saya secara eksplisit memutar kembali
Vignesh Kumar A
2
Saya telah mencoba pola ini tetapi tetap tidak berhasil - ketika saya memiliki transaksi eksternal, pola ini menciptakan titik simpanan dan jika terjadi kesalahan kritis (transaksi tidak dapat diselesaikan) kembalikan transaksi luar - ini masih menyebabkan @@ trancount = 1 sebelum masuk prosedur dan @@ trancount = 0 saat keluar
sparrow
3
Saya pikir bit di CATCH ini salah: if @xstate = -1 rollback; Melihat contoh MSDN ini , kita tidak boleh mengembalikan transaksi penuh kecuali tidak ada transaksi luar (kecuali kita melakukannya begin tran). Saya pikir prosedurnya hanya rollbackjika kita memulai transaksi, yang akan memperbaiki masalah @ sparrow.
Nick
62

Saya punya masalah ini juga. Bagi saya, alasannya adalah yang saya lakukan

return
commit

dari pada

commit
return   

dalam satu prosedur tersimpan.

seguso
sumber
4
@ Seguso - ini sangat membantu. Terima kasih sudah berbagi. Terkadang sesuatu yang begitu sederhana masuk ke dalam debu. Terjadi pada yang terbaik dari mereka.
Leo Gurdian
Ini adalah masalahnya bagi saya, tetapi kurang jelas karena kami menggabungkan beberapa panggilan sproc dalam satu transaksi besar melalui lapisan akses data kami - jadi hanya dengan melihat sproc Anda tidak dapat mengatakan bahwa ada transaksi sama sekali. Jika Anda mengalami masalah ini, pastikan tidak ada sesuatu di luar sproc itu sendiri yang membuat transaksi. Jika ada maka Anda mungkin tidak dapat menggunakan pernyataan return dalam sproc sama sekali.
EF0
Ini adalah saya, saya memiliki transaksi dan kembali sebelum saya melakukan transaksi dalam pernyataan if / else
Kevin
19

Hal ini biasanya terjadi ketika transaksi dimulai dan tidak dilakukan atau bukan rollback.

Jika terjadi kesalahan dalam prosedur tersimpan Anda, ini dapat mengunci tabel database karena transaksi tidak diselesaikan karena beberapa kesalahan runtime tanpa adanya penanganan pengecualian. Anda dapat menggunakan penanganan Exception seperti di bawah ini. SETEL XACT_ABORT

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

Sumber

Amarnath Balasubramanian
sumber
Jika ini masalahnya, pertanyaan / jawaban yang dikutip mungkin berarti pertanyaan / jawaban ini harus ditandai sebagai duplikat dan ditutup
Mark Schultheiss
10

Ketahuilah bahwa jika Anda menggunakan transaksi bertingkat, operasi ROLLBACK akan mengembalikan semua transaksi bertingkat termasuk yang paling luar.

Ini mungkin, dengan penggunaan dalam kombinasi dengan TRY / CATCH, menghasilkan kesalahan yang Anda jelaskan. Lihat selengkapnya di sini .

niklasolsn
sumber
5

Ini juga dapat terjadi jika prosedur tersimpan Anda mengalami kegagalan kompilasi setelah membuka transaksi (mis. Tabel tidak ditemukan, nama kolom tidak valid).

Saya menemukan saya harus menggunakan 2 prosedur yang tersimpan, satu "pekerja" dan satu pembungkus dengan coba / tangkap keduanya dengan logika yang mirip dengan yang diuraikan oleh Remus Rusanu. Tangkapan pekerja digunakan untuk menangani kegagalan "normal" dan tangkapan pembungkus untuk menangani kesalahan kegagalan kompilasi.

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

Kesalahan Tidak Dipengaruhi oleh COBALAH… CATCH Construct

Jenis kesalahan berikut tidak ditangani oleh blok CATCH ketika terjadi pada tingkat eksekusi yang sama seperti konstruksi TRY… CATCH:

  • Mengompilasi kesalahan, seperti kesalahan sintaksis , yang mencegah batch berjalan.
  • Kesalahan yang terjadi selama rekompilasi tingkat pernyataan, seperti kesalahan resolusi nama objek yang terjadi setelah kompilasi karena resolusi nama yang ditangguhkan.

Semoga ini membantu orang lain menghemat beberapa jam debugging ...

Justin
sumber
1
Terima kasih Justin. Pengamatan yang bagus. Dalam kasus saya, saya melakukan agregat di dalam pembaruan yang tidak menghasilkan kesalahan kompilasi selama penyimpanan SP tetapi memang sintaks yang tidak valid - "Agregat mungkin tidak muncul dalam daftar set pernyataan UPDATE"
kuklei
4

Dalam kasus saya, kesalahan itu disebabkan oleh bagian RETURNdalam BEGIN TRANSACTION. Jadi saya punya sesuatu seperti ini:

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

dan itu harus:

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

Bagi saya setelah debugging ekstensif, perbaikan itu hanyalah lemparan yang hilang; pernyataan di tangkapan setelah rollback. Tanpanya pesan kesalahan jelek ini akan berakhir dengan Anda.

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

Saya mendapatkan pesan kesalahan yang sama, kesalahan saya adalah saya mendapat titik koma di akhir baris COMMIT TRANSACTION

Zsombor Zsuffa
sumber
Sesederhana ini. Plus, kasus saya membutuhkan pernyataan 'ROLLBACK' jika SP tidak akan sepenuhnya dieksekusi. Hanya untuk menutup / mengakhiri transaksi.
J Cordero
1

Saya mengalami kesalahan ini sekali setelah menghilangkan pernyataan ini dari transaksi saya.

COMMIT TRANSACTION [MyTransactionName]
Ken Palmer
sumber
1

Menurut pendapat saya jawaban yang diterima dalam banyak kasus berlebihan.

Penyebab error seringnya ketidakcocokan antara BEGIN dan COMMIT sebagaimana dinyatakan dengan jelas oleh error tersebut. Ini berarti menggunakan:

Begin
  Begin
    -- your query here
  End
commit

dari pada

Begin Transaction
  Begin
    -- your query here
  End
commit

menghilangkan Transaksi setelah Mulai menyebabkan kesalahan ini!

omostan
sumber
1

Pastikan Anda tidak memiliki beberapa transaksi dalam prosedur / kueri yang sama di mana satu atau lebih transaksi tidak terikat.

Dalam kasus saya, saya secara tidak sengaja memiliki pernyataan BEGIN TRAN dalam kueri

Sen Alexandru
sumber
1

Ini juga dapat bergantung pada cara Anda memanggil SP dari kode C # Anda. Jika SP mengembalikan beberapa nilai jenis tabel kemudian memanggil SP dengan ExecuteStoreQuery, dan jika SP tidak mengembalikan nilai apa pun, panggil SP dengan ExecuteStoreCommand

Rajan Tikare
sumber
1

Hindari pemakaian

RETURN

pernyataan saat Anda menggunakan

BEGIN TRY
    ... 
END TRY

BEGIN CATCH
    ...
END CATCH

dan

BEGIN, COMMIT & ROLLBACK

pernyataan dalam prosedur tersimpan SQL

Nitin Patwekari
sumber
0

Jika Anda memiliki struktur kode seperti:

SELECT 151
RETURN -151

Kemudian gunakan:

SELECT 151
ROLLBACK
RETURN -151
Vidyesh
sumber