Eu tenho um procedimento armazenado que executa apenas 3 procedimentos armazenados dentro deles. Estou usando apenas 1 parâmetro para armazenar se o SP mestre for bem-sucedido.
Se o primeiro procedimento armazenado funcionar bem no procedimento mestre armazenado, mas o segundo procedimento armazenado falhar, ele reverterá automaticamente todos os SPs no SP mestre ou preciso executar algum comando?
Aqui está o meu procedimento:
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
fonte
fonte
spNewBilling3
gera um erro, mas você não deseja reverterspNewBilling2
ouspNewBilling1
, basta remover[begin|rollback|commit] transaction createSavebillinginvoice
despSavesomename
.Respostas:
Dado apenas o código mostrado na pergunta e supondo que nenhum dos três subprocessos tenha manipulação de transação explícita, sim, um erro em qualquer um dos três subprocessos será capturado e
ROLLBACK
oCATCH
bloco reverterá tudo do trabalho.MAS, aqui estão algumas coisas a serem observadas sobre transações (pelo menos no SQL Server):
Existe apenas uma transação real (a primeira), não importa quantas vezes você ligue
BEGIN TRAN
BEGIN TRAN
, independentemente de seu nome, o contador de transações é incrementado em 1.SELECT @@TRANCOUNT;
COMMIT
comando emitido quando@@TRANCOUNT
está em 2 ou acima não faz nada além de reduzir, um de cada vez, o contador de transações.COMMIT
é emitido quando o@@TRANCOUNT
é em1
Os pontos de economia permitem criar um subconjunto de trabalho dentro da transação que pode ser desfeito.
SAVE TRAN {save_point_name}
comandoROLLBACK {save_point_name}
. (mais sobre isso abaixo)SAVE TRAN {save_point_name}
, incluindo os pontos de salvamento criados após a criação do retrocesso (daí o "aninhamento").SAVE TRAN
não pode ser desfeito, exceto pela emissão de umaROLLBACK
transação completa.COMMIT
quando@@TRANCOUNT
é igual a 2 ou superior, não afeta os pontos de salvamento (porque, novamente, os níveis de transação acima de 1 não existem fora desse contador).Você não pode confirmar transações nomeadas específicas. A transação "nome", se fornecida junto com o
COMMIT
, é ignorada e existe apenas para facilitar a leitura.Um
ROLLBACK
emitido sem nome sempre reverterá TODAS as transações.Um
ROLLBACK
emitido com um nome deve corresponder a:Supondo que não
SAVE TRAN
tenha sido chamado com o mesmo nome de transação, isso reverterá TODAS as transações.esse comportamento "desfaz" todas as alterações feitas desde que a mais recente
SAVE TRAN {save_point_name}
foi chamada.SAVE TRAN
comandos emitidos com seu nome, cada ROLLBACK desse nome de transação desfará cada ponto de salvamento até que não haja mais esse nome. Depois disso, um ROLLBACK emitido com esse nome reverterá TODAS as transações.Por exemplo, suponha que os seguintes comandos foram executados na ordem mostrada:
Agora, se você emitir (cada um dos seguintes cenários é independente um do outro):
ROLLBACK TRAN B
uma vez: desfaz a "Consulta 4 da DML".@@TRANCOUNT
ainda é 2.ROLLBACK TRAN B
duas vezes: desfaz a "Consulta DML 4" e erro, pois não há um ponto de salvamento correspondente para "B".@@TRANCOUNT
ainda é 2.ROLLBACK TRAN A
uma vez: desfará "DML Query 4" e "DML Query 3".@@TRANCOUNT
ainda é 2.ROLLBACK TRAN A
duas vezes: desfará "DML Query 4", "DML Query 3" e "DML Query 2".@@TRANCOUNT
ainda é 2.ROLLBACK TRAN A
três vezes: desfaz "Consulta DML 4", "Consulta DML 3" e "Consulta DML 2". Em seguida, ele reverterá a transação inteira (tudo o que restou foi "Consulta DML 1").@@TRANCOUNT
agora é 0.COMMIT
uma vez:@@TRANCOUNT
desce para 1.COMMIT
uma vez e depoisROLLBACK TRAN B
uma vez:@@TRANCOUNT
desce para 1. Em seguida, desfaz a "Consulta DML 4" (provando que o COMMIT não fez nada).@@TRANCOUNT
ainda é 1.Nomes de transações e nomes de pontos de salvamento:
Um procedimento armazenado não é, por si só, uma transação implícita. Cada consulta, se nenhuma transação explícita foi iniciada, é uma transação implícita. É por isso que transações explícitas em torno de consultas únicas não são necessárias, a menos que possa haver um motivo programático para fazer isso
ROLLBACK
; caso contrário, qualquer erro na consulta é uma reversão automática dessa consulta.Ao chamar um procedimento armazenado, ele deve sair com o valor de
@@TRANCOUNT
ser o mesmo de quando foi chamado. Ou seja, você não pode:BEGIN TRAN
no processo sem confirmar, esperando confirmar no processo de chamada / pai.ROLLBACK
se uma transação explícita tiver sido iniciada antes do processo ser chamado, pois retornará@@TRANCOUNT
a 0.Se você sair de um procedimento armazenado com uma contagem de transações maior ou menor do que quando ele começou, você receberá um erro semelhante a:
Variáveis de tabela, assim como variáveis regulares, não são vinculadas por transações.
Em relação à manipulação de transações em procs que podem ser chamados de forma independente (e, portanto, precisam de manipulação de transações) ou chamadas de outros procs (portanto, não precisam de manipulação de transações): isso pode ser realizado de duas maneiras diferentes.
A maneira que eu tenho lidado com isso há vários anos, agora que parece funcionar bem, é apenas
BEGIN
/COMMIT
/ROLLBACK
na camada mais externa. Chamadas de subprocesso apenas ignoram os comandos de transação. Descrevi abaixo o que coloco em cada processo (bem, cada um que precisa de manipulação de transações).DECLARE @InNestedTransaction BIT;
Em vez de simples
BEGIN TRAN
, faça:Em vez de simples
COMMIT
, faça:Em vez de simples
ROLLBACK
, faça:Esse método deve funcionar da mesma maneira, independentemente de a transação ter sido iniciada no SQL Server ou se foi iniciada na camada de aplicativo.
Para obter o modelo completo desta manipulação de transações na
TRY...CATCH
construção, consulte minha resposta para a seguinte pergunta do DBA.SE: Somos obrigados a manipular transações no código C # e no procedimento armazenado .Indo além do "básico", existem algumas nuances adicionais de transações a serem observadas:
Por padrão, as Transações, na maioria das vezes, não são revertidas / canceladas automaticamente quando ocorre um erro. Isso geralmente não é um problema, desde que você tenha um tratamento de erros adequado e ligue para
ROLLBACK
si mesmo. No entanto, às vezes as coisas ficam complicadas, como no caso de erros de interrupção de lote ou ao usarOPENQUERY
(ou Servidores Vinculados em geral) e ocorre um erro no sistema remoto. Embora a maioria dos erros possa ser interceptadaTRY...CATCH
, existem dois que não podem ser interceptados dessa maneira (embora não se lembre de quais no momento - pesquisando). Nesses casos, você deve usarSET XACT_ABORT ON
para reverter corretamente a transação.SET XACT_ABORT ON faz o SQL Server reverter imediatamente qualquer transação (se uma estiver ativa) e abortar o lote se ocorrer algum erro. Essa configuração existia antes do SQL Server 2005, que introduziu a
TRY...CATCH
construção. Na maioria das vezes,TRY...CATCH
lida com a maioria das situações e, portanto, obsoleta a necessidadeXACT_ABORT ON
. No entanto, ao usarOPENQUERY
(e possivelmente um outro cenário que não me lembro no momento), você ainda precisará usá-loSET XACT_ABORT ON;
.Dentro de um gatilho,
XACT_ABORT
está implicitamente definido comoON
. Isso causa qualquer erro no gatilho para cancelar a instrução DML inteira que acionou o gatilho.Você sempre deve ter o tratamento de erros adequado, especialmente ao usar Transações. A
TRY...CATCH
construção, introduzida no SQL Server 2005, fornece um meio de lidar com quase todas as situações, uma melhoria bem-vinda sobre o teste@@ERROR
após cada instrução, o que não ajudou muito com erros de interrupção de lote.TRY...CATCH
introduziu um novo "estado", no entanto. Quando nãoTRY...CATCH
estiver usando a construção, se você tiver uma transação ativa e ocorrer um erro, existem vários caminhos que podem ser seguidos:XACT_ABORT OFF
e erro de cancelamento de instrução: a transação ainda está ativa e o processamento continua com a próxima instrução , se houver.XACT_ABORT OFF
e a maioria dos erros de interrupção de lote: a transação ainda está ativa e o processamento continua com o próximo lote , se houver.XACT_ABORT OFF
e certos erros de interrupção de lote: a transação é revertida e o processamento continua com o próximo lote , se houver.XACT_ABORT ON
e qualquer erro: a transação é revertida e o processamento continua com o próximo lote , se houver.NO ENTANTO, ao usar
TRY...CATCH
, os erros de interrupção de lote não abortam o lote, mas transferem o controle para oCATCH
bloco. QuandoXACT_ABORT
forOFF
, a Transação ainda estará ativa na grande maioria das vezes, e você precisaráCOMMIT
, ou provavelmenteROLLBACK
. Porém, ao encontrar certos erros de interrupção de lote (como comOPENQUERY
), ou quandoXACT_ABORT
estiverON
, a Transação estará em um novo estado, "não comprometível". Nesse estado, você não podeCOMMIT
, nem pode executar operações DML. Tudo o que você pode fazer éROLLBACK
eSELECT
declarações. No entanto, nesse estado "incompatível", a Transação foi revertida após o erro que ocorreu e emitir aROLLBACK
é apenas uma formalidade, mas que deve ser executada.Uma função, XACT_STATE , pode ser usada para determinar se uma transação está ativa, incompatível ou não existe. Recomenda-se (por alguns, pelo menos) verificar esta função no
CATCH
bloco para determinar se o resultado é-1
(ou seja, incompatível) em vez de testar se@@TRANCOUNT > 0
. Mas comXACT_ABORT ON
, esse deve ser o único estado possível, portanto parece que testar@@TRANCOUNT > 0
eXACT_STATE() <> 0
é equivalente. Por outro lado, quandoXACT_ABORT
existeOFF
e existe uma transação ativa, é possível ter um estado de um1
ou-1
de umCATCH
bloco, o que permite a possibilidade de emitir emCOMMIT
vez deROLLBACK
(embora, não consigo pensar em um caso para quando alguém gostaria deCOMMIT
se a transação for confirmada). Mais informações e pesquisas sobre o usoXACT_STATE()
em umCATCH
blocoXACT_ABORT ON
podem ser encontradas na minha resposta à seguinte pergunta do DBA.SE: Em que casos uma transação pode ser confirmada de dentro do bloco CATCH quando XACT_ABORT está definido como ON? . Observe que há um pequeno erroXACT_STATE()
que faz com que ele retorne falsamente1
em certos cenários: XACT_STATE () retorna 1 quando usado em SELECT com algumas variáveis do sistema, mas sem a cláusula FROMNotas sobre o código original:
BEGIN
e emEND
torno de cadaEXEC
chamadafonte
Compile errors, such as syntax errors, that prevent a batch from running
eErrors that occur during statement-level recompilation, such as object name resolution errors that occur after compilation because of deferred name resolution.
. Mas eles não acontecem com muita frequência e, quando você encontrar uma situação dessas, corrija-a (se houver um erro no código) ou coloque-a em um subprocesso (EXEC
ousp_executesql
) para queTRY...CATCH
possa prendê-la.Sim, se devido a algum código de reversão de erro na instrução catch do seu procedimento armazenado principal for executado, ele reverterá todas as operações executadas por qualquer instrução direta ou através de qualquer um dos seus procedimentos armazenados aninhados.
Mesmo que você não tenha aplicado nenhuma transação explícita nos procedimentos armazenados aninhados, esse procedimento armazenado usará a transação implícita e será confirmado na conclusão, MAS você confirmou por meio de transação explícita ou implícita nos procedimentos armazenados aninhados o mecanismo do SQL Server o ignorará e irá reverter todas as ações desses procedimentos armazenados aninhados se o procedimento armazenado principal falhar e a transação for suportada por rollback.
Toda vez que a transação é confirmada ou revertida com base na ação executada no final da transação mais externa. Se a transação externa for confirmada, as transações internas aninhadas também serão confirmadas. Se a transação externa for revertida, todas as transações internas também serão revertidas, independentemente de as transações internas terem sido confirmadas ou não individualmente.
Para referência http://technet.microsoft.com/en-us/library/ms189336(v=sql.105).aspx
fonte