У меня есть хранимая процедура, которая выполняет только 3 хранимые процедуры внутри них. Я использую только 1 параметр для хранения, если мастер SP успешно.
Если первая хранимая процедура работает нормально в основной хранимой процедуре, но 2-я хранимая процедура дает сбой, то она автоматически откатит все SP в главном SP или мне нужно будет выполнить какую-то команду?
Вот моя процедура:
CREATE PROCEDURE [dbo].[spSavesomename]
-- Add the parameters for the stored procedure here
@successful bit = null output
AS
BEGIN
begin transaction createSavebillinginvoice
begin Try
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
BEGIN
EXEC [dbo].[spNewBilling1]
END
BEGIN
EXEC [dbo].[spNewBilling2]
END
BEGIN
EXEC [dbo].[spNewBilling3]
END
set @successful = 1
end Try
begin Catch
rollback transaction createSavesomename
insert into dbo.tblErrorMessage(spName, errorMessage, systemDate)
values ('spSavesomename', ERROR_MESSAGE(), getdate())
return
end Catch
commit transaction createSavesomename
return
END
GO
sql-server
stored-procedures
transaction
user2483342
источник
источник
spNewBilling3
выдает ошибку, но вы не хотите откатspNewBilling2
илиspNewBilling1
, то просто удалите[begin|rollback|commit] transaction createSavebillinginvoice
изspSavesomename
.Ответы:
Учитывая только код, показанный в вопросе, и предполагая, что ни один из трех подпроцессов не имеет какой-либо явной обработки транзакций, тогда да, ошибка в любом из трех подпроцессов будет обнаружена, и блок
ROLLBACK
вCATCH
блоке откатит все работы.НО вот некоторые вещи, на которые следует обратить внимание в отношении транзакций (по крайней мере, в SQL Server):
Существует только одна реальная транзакция (первая), независимо от того, сколько раз вы звоните
BEGIN TRAN
BEGIN TRAN
, независимо от того, назван он или нет, счетчик транзакций увеличивается на 1.SELECT @@TRANCOUNT;
COMMIT
команды, введенные, когда значение@@TRANCOUNT
равно 2 или выше, не делают ничего, кроме как уменьшают, по одному, счетчик транзакций.COMMIT
не будет выдано , когда@@TRANCOUNT
находится в1
Сохранение точки позволяют создавать подмножество работы в рамках в операции , которая может быть отменена.
SAVE TRAN {save_point_name}
командыROLLBACK {save_point_name}
. (подробнее об этом ниже)SAVE TRAN {save_point_name}
, включая любые точки сохранения, созданные после создания отката (отсюда и «вложение»).SAVE TRAN
не может быть отменена, кроме как путем выдачи полнойROLLBACK
транзакции.COMMIT
когда@@TRANCOUNT
равен 2 или выше, не влияет на точки сохранения (потому что уровни транзакций выше 1 не существуют вне этого счетчика).Вы не можете совершать определенные именованные транзакции. «Имя» транзакции, если оно предоставлено вместе с
COMMIT
, игнорируется и существует только для удобства чтения.ROLLBACK
Выдается без имени всегда будет откатить все транзакции.ROLLBACK
Выдается имя должно соответствовать либо:если не
SAVE TRAN
было вызвано ни одного имени с таким же именем транзакции, это откатит ВСЕ транзакции.это поведение «отменяет» все изменения, сделанные с момента вызова самой последней версии
SAVE TRAN {save_point_name}
.SAVE TRAN
выданы команды с ее именем, то каждый ROLLBACK этого имени транзакции отменяет каждую точку сохранения, пока от этого имени не останется ни одной. После этого ROLLBACK, выпущенный с таким именем, откатит ВСЕ транзакции.Например, предположим, что следующие команды были запущены в указанном порядке:
Теперь, если вы выдадите запрос (каждый из следующих сценариев не зависит друг от друга):
ROLLBACK TRAN B
один раз: это отменит "DML Query 4".@@TRANCOUNT
еще 2.ROLLBACK TRAN B
дважды: отменяется «DML Query 4», а затем происходит ошибка, поскольку для «B» нет соответствующей точки сохранения.@@TRANCOUNT
еще 2.ROLLBACK TRAN A
один раз: он отменяет «DML Query 4» и «DML Query 3».@@TRANCOUNT
еще 2.ROLLBACK TRAN A
дважды: отменит «DML Query 4», «DML Query 3» и «DML Query 2».@@TRANCOUNT
еще 2.ROLLBACK TRAN A
трижды: он отменяет «DML Query 4», «DML Query 3» и «DML Query 2». Затем он откатит всю транзакцию (все, что осталось, это «DML Query 1»).@@TRANCOUNT
сейчас 0.COMMIT
один раз:@@TRANCOUNT
снижается до 1.COMMIT
один раз, а затемROLLBACK TRAN B
один раз:@@TRANCOUNT
уменьшается до 1. Затем он отменяет «DML Query 4» (доказывая, что COMMIT ничего не делал).@@TRANCOUNT
все еще 1.Имена транзакций и имена точек сохранения:
Хранимая процедура сама по себе не является неявной транзакцией. Каждый запрос, если явная транзакция не была запущена, является неявной транзакцией. Вот почему явные транзакции вокруг отдельных запросов не нужны, если только для этого не может быть программной причины, в
ROLLBACK
противном случае любая ошибка в запросе является автоматическим откатом этого запроса.При вызове хранимой процедуры она должна завершиться со значением
@@TRANCOUNT
, совпадающим с тем, когда она была вызвана. Это означает, что вы не можете:BEGIN TRAN
в proc без фиксации, ожидая фиксации в вызывающем / родительском процессе.ROLLBACK
если явная транзакция была запущена до вызова процедуры, так как она вернется@@TRANCOUNT
к 0.Если вы выйдете из хранимой процедуры с количеством транзакций, которое будет выше или ниже, чем при просмотре, вы получите ошибку, похожую на:
Табличные переменные, как и обычные переменные, не связаны транзакциями.
Что касается обработки транзакций в процессах, которые могут вызываться независимо (и, следовательно, требовать обработки транзакций) или вызываться из других процедур (следовательно, не требуется обработка транзакций): это может быть выполнено несколькими различными способами.
То, как я справляюсь с этим уже несколько лет, похоже, работает хорошо, только
BEGIN
/COMMIT
/ROLLBACK
на самом внешнем слое. Вызовы sub-proc просто пропускают команды транзакции. Ниже я изложил, что я вкладываю в каждый процесс (ну, каждый, который нуждается в обработке транзакций).DECLARE @InNestedTransaction BIT;
Вместо простого
BEGIN TRAN
сделайте:Вместо простого
COMMIT
сделайте:Вместо простого
ROLLBACK
сделайте:Этот метод должен работать одинаково независимо от того, была ли транзакция запущена в SQL Server или запущена на уровне приложения.
Для полного шаблона этой обработки транзакций в
TRY...CATCH
конструкции, пожалуйста, смотрите мой ответ на следующий вопрос DBA.SE: Требуем ли мы обрабатывать транзакции в C # Code, а также в хранимых процедурах .Выходя за рамки «основ», необходимо учитывать некоторые дополнительные нюансы транзакций:
По умолчанию транзакции в большинстве случаев автоматически не откатываются / отменяются при возникновении ошибки. Обычно это не проблема, если вы правильно обрабатываете ошибки и звоните
ROLLBACK
сами. Однако иногда все усложняется, например, в случае ошибок пакетного прерывания или при использованииOPENQUERY
(или связанных серверов в целом), и в удаленной системе возникает ошибка. Хотя большинство ошибок можно отследить с помощьюTRY...CATCH
, есть две, которые не могут быть перехвачены таким образом (хотя не могу вспомнить, какие из них на данный момент - исследование). В этих случаях вы должны использоватьSET XACT_ABORT ON
для правильного отката транзакции.SET XACT_ABORT ON заставляет SQL Server , чтобы немедленно откат любой сделки (если он активен) и преждевременное прекращение партии , если любая ошибка происходит. Этот параметр существовал до SQL Server 2005, в котором была представлена
TRY...CATCH
конструкция. По большей части,TRY...CATCH
обрабатывает большинство ситуаций и поэтому в основном устраняет необходимостьXACT_ABORT ON
. Тем не менее, при использованииOPENQUERY
(и, возможно, еще один сценарий, который я не могу вспомнить в данный момент), вам все равно придется использоватьSET XACT_ABORT ON;
.Внутри Trigger
XACT_ABORT
неявно установлено значениеON
. Это вызывает любую ошибку в Trigger, чтобы отменить весь оператор DML, который запустил Trigger.У вас всегда должна быть правильная обработка ошибок, особенно при использовании транзакций.
TRY...CATCH
Конструкт, введенный в SQL Server 2005 предоставляет средства обработки почти во всех ситуациях улучшения приветственного по тестированию для@@ERROR
после каждого заявления, которое не помогло много с партиями прерывания ошибок.TRY...CATCH
представил новое "государство", однако. Если конструкция не используетсяTRY...CATCH
, если у вас есть активная заявка и возникает ошибка, существует несколько путей:XACT_ABORT OFF
и ошибка прерывания оператора: транзакция все еще активна, и обработка продолжается со следующего оператора , если таковой имеется.XACT_ABORT OFF
и большинство ошибок при пакетном прерывании: транзакция все еще активна, и обработка продолжается со следующего пакета , если он есть.XACT_ABORT OFF
и некоторые ошибки прерывания пакета: откат транзакции и обработка продолжается со следующего пакета , если он есть.XACT_ABORT ON
и любая ошибка: транзакция откатывается, и обработка продолжается со следующей партии , если таковая имеется.ОДНАКО, при использовании
TRY...CATCH
ошибки прерывания пакета не отменяют пакет, а передают управлениеCATCH
блоку. КогдаXACT_ABORT
этоOFF
, транзакция будет по- прежнему активны подавляющее большинство времени, и вы должныCOMMIT
, или , скорее всего,ROLLBACK
. Но при обнаружении определенных ошибок пакетного прерывания (например, сOPENQUERY
) или когдаXACT_ABORT
естьON
, Транзакция будет в новом состоянии, «uncommitable». В этом состоянии вы не можете и неCOMMIT
можете выполнять какие-либо операции DML. Все, что вы можете сделать, этоROLLBACK
иSELECT
заявления. Однако в этом «неудобном» состоянии транзакция была откатана после возникновения ошибки, и выдача -ROLLBACK
это просто формальность, но она должна быть выполнена.Функция XACT_STATE может использоваться для определения, активна ли транзакция, не отправлена или не существует. Рекомендуется (по крайней мере, для некоторых) проверить эту функцию в
CATCH
блоке, чтобы определить, является ли результат-1
(то есть недоступным) вместо проверки, если@@TRANCOUNT > 0
. Но сXACT_ABORT ON
этим должно быть единственно возможное состояние, поэтому кажется, что тестирование@@TRANCOUNT > 0
иXACT_STATE() <> 0
эквивалентны. С другой стороны, когдаXACT_ABORT
естьOFF
и есть активная заявка, то возможно иметь состояние одного1
или-1
вCATCH
блоке, что допускает возможность выдачиCOMMIT
вместоROLLBACK
(хотя, я не могу вспомнить случай, когда кто-то хотел быCOMMIT
если Сделка является обязательной). Дополнительную информацию и исследования по использованиюXACT_STATE()
вCATCH
блоке сXACT_ABORT ON
можно найти в моем ответе на следующий вопрос DBA.SE: В каких случаях транзакция может быть зафиксирована изнутри блока CATCH, когда для XACT_ABORT установлено значение ON? , Обратите внимание, что существует небольшая ошибка,XACT_STATE()
которая приводит к ложному возвращению1
в некоторых сценариях: XACT_STATE () возвращает 1, когда используется в SELECT с некоторыми системными переменными, но без предложения FROMПримечания об оригинальном коде:
BEGIN
иEND
вокруг каждогоEXEC
звонкаисточник
Compile errors, such as syntax errors, that prevent a batch from running
иErrors that occur during statement-level recompilation, such as object name resolution errors that occur after compilation because of deferred name resolution.
. Но они случаются не очень часто, и когда вы обнаружите такую ситуацию, либо исправьте ее (если это ошибка в коде), либо поместите ее в подпроцесс (EXEC
илиsp_executesql
), чтобыTRY...CATCH
можно было ее перехватить.Да, если из-за какого-либо кода отката ошибки в операторе catch вашей главной хранимой процедуры будет выполнено выполнение, он откатит все операции, выполненные любым прямым оператором или через любую из ваших вложенных хранимых процедур в нем.
Даже если вы не применили ни одной явной транзакции в своих вложенных хранимых процедурах, эти хранимые процедуры будут использовать неявную транзакцию и будут выполнять ее после завершения, НО вы либо зафиксировали явную или неявную транзакцию во вложенных хранимых процедурах, механизм SQL Server будет игнорировать ее и будет откатить все действия этих вложенных хранимых процедур, если главная хранимая процедура не выполнена и транзакция откатывается.
Каждый раз, когда транзакция либо фиксируется, либо откатывается на основании действия, выполненного в конце самой внешней транзакции. Если внешняя транзакция фиксируется, внутренние вложенные транзакции также фиксируются. Если внешняя транзакция откатывается, то все внутренние транзакции также откатываются независимо от того, были ли внутренние транзакции зафиксированы по отдельности.
Для справки http://technet.microsoft.com/en-us/library/ms189336(v=sql.105).aspx
источник