Saya baru saja terkejut dengan sesuatu di TSQL. Saya pikir jika xact_abort aktif, memanggil sesuatu seperti
raiserror('Something bad happened', 16, 1);
akan menghentikan eksekusi prosedur yang disimpan (atau batch apa pun).
Tapi pesan kesalahan ADO.NET saya justru membuktikan sebaliknya. Saya mendapat pesan kesalahan penyebab kesalahan dalam pesan pengecualian, ditambah hal berikutnya yang rusak setelah itu.
Ini adalah solusi saya (yang merupakan kebiasaan saya), tetapi sepertinya itu tidak perlu:
if @somethingBadHappened
begin;
raiserror('Something bad happened', 16, 1);
return;
end;
Dokumen mengatakan ini:
Ketika SET XACT_ABORT AKTIF, jika pernyataan Transact-SQL memunculkan kesalahan run-time, seluruh transaksi dihentikan dan dibatalkan.
Apakah itu berarti saya harus menggunakan transaksi eksplisit?
sql
sql-server
tsql
Eric Z Beard
sumber
sumber
RAISERROR
sebenarnya akan menghentikan eksekusi jika tingkat keparahan disetel ke 17 atau 18, bukan 16RAISERROR
sebenarnya tidak akan menghentikan eksekusi jika tingkat keparahan disetel ke 17 atau 18, bukan 16.Messages
tab Anda tidak akan melihat(X rows affected)
atauPRINT
pesan, yang menurut saya adalah kebohongan total !Jawaban:
Ini adalah By Design TM , seperti yang Anda lihat di Connect by the SQL Server tim menanggapi pertanyaan serupa:
Ya, ini sedikit masalah bagi beberapa orang yang berharap
RAISERROR
dengan tingkat keparahan tinggi (seperti16
) akan sama dengan kesalahan eksekusi SQL - ternyata tidak.Solusi Anda hanya tentang apa yang perlu Anda lakukan, dan menggunakan transaksi eksplisit tidak berpengaruh pada perilaku yang ingin Anda ubah.
sumber
Jika Anda menggunakan blok try / catch, nomor kesalahan raiserror dengan tingkat keparahan 11-19 akan menyebabkan eksekusi melompat ke blok catch.
Setiap tingkat keparahan di atas 16 adalah kesalahan sistem. Untuk mendemonstrasikan kode berikut, siapkan blok coba / tangkap dan jalankan prosedur tersimpan yang kami anggap akan gagal:
asumsikan kita memiliki tabel [dbo]. [Kesalahan] untuk menahan kesalahan asumsikan kita memiliki prosedur tersimpan [dbo]. [AssumeThisFails] yang akan gagal saat kita menjalankannya
-- first lets build a temporary table to hold errors if (object_id('tempdb..#RAISERRORS') is null) create table #RAISERRORS (ErrorNumber int, ErrorMessage varchar(400), ErrorSeverity int, ErrorState int, ErrorLine int, ErrorProcedure varchar(128)); -- this will determine if the transaction level of the query to programatically determine if we need to begin a new transaction or create a save point to rollback to declare @tc as int; set @tc = @@trancount; if (@tc = 0) begin transaction; else save transaction myTransaction; -- the code in the try block will be executed begin try declare @return_value = '0'; set @return_value = '0'; declare @ErrorNumber as int, @ErrorMessage as varchar(400), @ErrorSeverity as int, @ErrorState as int, @ErrorLine as int, @ErrorProcedure as varchar(128); -- assume that this procedure fails... exec @return_value = [dbo].[AssumeThisFails] if (@return_value <> 0) raiserror('This is my error message', 17, 1); -- the error severity of 17 will be considered a system error execution of this query will skip the following statements and resume at the begin catch block if (@tc = 0) commit transaction; return(0); end try -- the code in the catch block will be executed on raiserror("message", 17, 1) begin catch select @ErrorNumber = ERROR_NUMBER(), @ErrorMessage = ERROR_MESSAGE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(), @ErrorLine = ERROR_LINE(), @ErrorProcedure = ERROR_PROCEDURE(); insert #RAISERRORS (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure) values (@ErrorNumber, @ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorLine, @ErrorProcedure); -- if i started the transaction if (@tc = 0) begin if (XACT_STATE() <> 0) begin select * from #RAISERRORS; rollback transaction; insert into [dbo].[Errors] (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure) select * from #RAISERRORS; insert [dbo].[Errors] (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure) values (@ErrorNumber, @ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorLine, @ErrorProcedure); return(1); end end -- if i didn't start the transaction if (XACT_STATE() = 1) begin rollback transaction myTransaction; if (object_id('tempdb..#RAISERRORS') is not null) insert #RAISERRORS (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure) values (@ErrorNumber, @ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorLine, @ErrorProcedure); else raiserror(@ErrorMessage, @ErrorSeverity, @ErrorState); return(2); end else if (XACT_STATE() = -1) begin rollback transaction; if (object_id('tempdb..#RAISERRORS') is not null) insert #RAISERRORS (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure) values (@ErrorNumber, @ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorLine, @ErrorProcedure); else raiserror(@ErrorMessage, @ErrorSeverity, @ErrorState); return(3); end end catch end
sumber
Gunakan
RETURN
segera setelahnyaRAISERROR()
dan itu tidak akan menjalankan prosedur lebih lanjut.sumber
rollback transaction
sebelum meneleponreturn
.Seperti yang ditunjukkan di dokumen untuk
SET XACT_ABORT
,THROW
pernyataan itu harus digunakan sebagai penggantiRAISERROR
.Keduanya berperilaku sedikit berbeda . Namun bila
XACT_ABORT
disetel ke ON, maka Anda harus selalu menggunakanTHROW
perintah.sumber