Вложенные хранимые процедуры, содержащие шаблон отката TRY CATCH?
меня интересуют побочные эффекты и потенциальные проблемы следующим образом:
CREATE PROCEDURE [Name]
AS
BEGIN
BEGIN TRANSACTION
BEGIN TRY
[...Perform work, call nested procedures...]
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
RAISERROR [rethrow caught error using @ErrorNumber, @ErrorMessage, etc]
END CATCH
END
насколько я понимаю, этот шаблон звучит при использовании с одной процедурой-процедура либо завершит все свои утверждения без ошибок, либо откатит все действия и сообщит об ошибке.
однако, когда одна хранимая процедура вызывает другую хранимую процедуру, чтобы выполнить некоторую подблоку работы (с пониманием того, что меньшая процедура иногда вызывается сама по себе) я вижу проблему, возникающую в связи с откатами - выдается информационное сообщение (Уровень 16) с указанием The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.
. Это я предполагаю, потому что откат в подпроцедуре всегда откатывает самую внешнюю транзакцию, а не только транзакцию, запущенную в подпроцедуре.
Я хочу, чтобы все это откатилось и прервалось, если произойдет какая-либо ошибка (и ошибка, сообщенная клиенту как ошибка SQL), я просто не уверен во всем побочные эффекты, которые исходят от внешних слоев, пытающихся откатить транзакцию,которая уже была откатана. Возможно, проверка @@TRANCOUNT
перед выполнением отката на каждом уровне try CATCH?
наконец, есть клиентский конец (Linq2SQL), который имеет свой собственный уровень транзакций:
try
{
var context = new MyDataContext();
using (var transaction = new TransactionScope())
{
// Some Linq stuff
context.SubmitChanges();
context.MyStoredProcedure();
transactionComplete();
}
}
catch
{
// An error occured!
}
в случае, если хранимая процедура, "MySubProcedure", называется внутри MyStoredProcedure вызывает ошибку, могу ли я быть уверен, что все ранее сделанное в Mystoredprocedure будет откат, все операции Linq, выполненные SubmitChanges, будут откатаны, и, наконец, что ошибка будет зарегистрирована? Или что мне нужно изменить в моем шаблоне, чтобы обеспечить атомарность всей операции, при этом позволяя использовать дочерние части по отдельности (т. е. подпроцедуры все равно должны иметь одинаковую атомную защиту)
5 ответов:
это наш шаблон (журнал ошибок удален)
это предназначено для обработки
- статья Пола Рэндала "нет такой вещи, как вложенная транзакция в SQL Server"
- ошибка 266
- Триггер Откаты
объяснениями:
все TXN begin и commit/rollbacks должны быть сопряжены так, чтобы
@@TRANCOUNT
то же самое при входе и выходнесоответствий
@@TRANCOUNT
вызвать ошибку 266, потому что
BEGIN TRAN
С шагом@@TRANCOUNT
COMMIT
уменьшается@@TRANCOUNT
ROLLBACK
возвращает@@TRANCOUNT
к нулювы не можете уменьшить
@@TRANCOUNT
для текущей области
Это то, что вы думаете, является " внутренним сделка"
SET XACT_ABORT ON
подавляет ошибку 266, вызванную несоответствием@@TRANCOUNT
А также занимается такими вопросами, как это "тайм-аут транзакции SQL Server" ВКЛ dba.seэто позволяет для TXNs стороны клиента (как LINQ) Одна хранимая процедура может быть частью распределенной или XA-транзакции или просто инициирована в клиентском коде (например, .net TransactionScope)
использование:
- каждый сохраненный процесс должен соответствовать одному и тому же шаблону
резюме
- так что не создавайте больше TXNs, чем вам нужно
код
CREATE PROCEDURE [Name] AS SET XACT_ABORT, NOCOUNT ON DECLARE @starttrancount int BEGIN TRY SELECT @starttrancount = @@TRANCOUNT IF @starttrancount = 0 BEGIN TRANSACTION [...Perform work, call nested procedures...] IF @starttrancount = 0 COMMIT TRANSACTION END TRY BEGIN CATCH IF XACT_STATE() <> 0 AND @starttrancount = 0 ROLLBACK TRANSACTION; THROW; --before SQL Server 2012 use --RAISERROR [rethrow caught error using @ErrorNumber, @ErrorMessage, etc] END CATCH GO
Примечания:
проверка отката на самом деле избыточна из-за
SET XACT_ABORT ON
. Тем не менее, это заставляет меня чувствовать себя лучше, выглядит странно без, и позволяет в ситуациях, когда вы не хотите его наРемус Rusanu есть похожие раковины который использует точки сохранения. Я предпочитаю атомарный вызов DB и не использую частичные обновления, такие как их статья
Я не парень Linq (и не Эрланд), но он написал абсолютные Библии по обработке ошибок. Вне осложнений Linq может добавить к вашей проблеме, все ваши другие вопросы должны быть даны ответы здесь:
http://www.sommarskog.se/error_handling/Part1.html
(старая ссылка: http://www.sommarskog.se/error_handling_2005.html)
чтобы решить проблему возврата номера ошибки и номера строки, упомянутого @AlexKuznetsov, можно поднять ошибку как таковую:
DECLARE @ErrorMessage NVARCHAR(4000) DECLARE @ErrorSeverity INT DECLARE @ErrorState INT DECLARE @ErrorLine INT DECLARE @ErrorNumber INT SELECT @ErrorMessage = ERROR_MESSAGE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(), @ErrorNumber = ERROR_NUMBER(), @ErrorLine = ERROR_LINE() RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorNumber, @ErrorLine)
-- @Аманда метод выше не возвращает правильный номер ошибки
DECLARE @ErrorMessage nvarchar(4000), @ErrorSeverity int, @ErrorState int, @ErrorLine int, @ErrorNumber int BEGIN TRY SELECT 1/0; -- CATCH me END TRY BEGIN CATCH DECLARE @err int = @@ERROR PRINT @err -- 8134, divide by zero PRINT ERROR_NUMBER() -- 8134 SELECT @ErrorMessage = ERROR_MESSAGE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(), @ErrorNumber = ERROR_NUMBER(), @ErrorLine = ERROR_LINE() -- error number = 50000 :( RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorNumber, @ErrorLine) END CATCH -- error number = 8134 SELECT 1/0
в случае, если в CATCH не требуется специальная обработка ошибок, кроме retrow и сохраненной цепочки вызовов procs, не слишком длинная, может быть целесообразно использовать такой простой шаблон:
create procedure someNestedSP as SET XACT_ABORT ON begin transaction -- do some work or call some other similar SP commit transaction
Он также откатывает корневую транзакцию со всеми "вложенными" в случае любой ошибки, но код короче и проще, чем решение @gbn. И все же
XACT_ABORT
заботится о большинстве вопросов, упомянутых там.могут быть дополнительные накладные расходы для вложенности транзакций, но это может быть не так наверное, слишком высоко.