Меня только что кое-что удивило в TSQL. Я думал, что если был включен xact_abort, вызывая что-то вроде
raiserror('Something bad happened', 16, 1);
остановит выполнение хранимой процедуры (или любого пакета).
Но мое сообщение об ошибке ADO.NET просто доказывает обратное. Я получил как сообщение об ошибке raiserror в сообщении об исключении, так и следующее, что сломалось после этого.
Это мой обходной путь (в любом случае это моя привычка), но не похоже, что это необходимо:
if @somethingBadHappened
begin;
raiserror('Something bad happened', 16, 1);
return;
end;
В документах говорится следующее:
Если для SET XACT_ABORT установлено значение ON, если инструкция Transact-SQL вызывает ошибку времени выполнения, вся транзакция завершается и выполняется откат.
Означает ли это, что я должен использовать явную транзакцию?
sql
sql-server
tsql
Эрик Зи Берд
источник
источник
RAISERROR
фактически прекратит выполнение, если уровень серьезности установлен на 17 или 18 вместо 16.RAISERROR
фактически не прерывает выполнение, если уровень серьезности установлен на 17 или 18 вместо 16.Messages
вкладку, вы не увидите сообщений(X rows affected)
илиPRINT
, что я бы сказал, полная ложь !Ответы:
Это By Design TM , как вы можете видеть в разделе Connect ответ команды SQL Server на аналогичный вопрос:
Да, это небольшая проблема для тех, кто надеялся,
RAISERROR
что высокая степень серьезности (например,16
) будет такой же, как ошибка выполнения SQL - это не так.Ваш обходной путь - это именно то, что вам нужно сделать, и использование явной транзакции не влияет на поведение, которое вы хотите изменить.
источник
Если вы используете блок try / catch, номер ошибки повышения уровня серьезности 11-19 приведет к переходу выполнения к блоку catch.
Любая серьезность выше 16 является системной ошибкой. Чтобы продемонстрировать следующий код, настраивает блок try / catch и выполняет хранимую процедуру, которая, как мы предполагаем, завершится ошибкой:
предположим, что у нас есть таблица [dbo]. [Errors] для хранения ошибок предположим, что у нас есть хранимая процедура [dbo]. [AssumeThisFails], которая завершится ошибкой, когда мы ее выполним
-- 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
источник
Используйте
RETURN
сразу после,RAISERROR()
и процедура не будет выполняться дальше.источник
rollback transaction
прежде чем звонитьreturn
.Как указано в документации для
SET XACT_ABORT
,THROW
оператор следует использовать вместоRAISERROR
.Эти двое ведут себя немного по-разному . Но когда
XACT_ABORT
установлено значение ON, вы всегда должны использоватьTHROW
команду.источник