Não use uma transação para o procedimento armazenado
18
Eu tenho um procedimento armazenado que executa alguns comandos. Não quero que esses comandos sejam agrupados na transação do procedimento armazenado. Se o 4º comando falhar, quero que o 1º, o 2º e o 3º permaneçam, e não a reversão.
É possível gravar o procedimento armazenado de forma que nem todos sejam executados como uma grande transação?
Todas as transações não serão executadas em uma única transação. Veja este exemplo:
use TestDB;
go
ifexists(select1from sys.tables where object_id = object_id('dbo.TestTranTable1'))droptable dbo.TestTranTable1;createtable dbo.TestTranTable1
(
id int identity(1,1)notnull,
some_int int notnulldefault1);
go
insertinto dbo.TestTranTable1
defaultvalues;
go 4select*from dbo.TestTranTable1;ifexists(select1from sys.sql_modules where object_id = object_id('dbo.ChangeValues'))begindropproc dbo.ChangeValues;end
go
createproc dbo.ChangeValues
asupdate dbo.TestTranTable1
set some_int =11where id =1;update dbo.TestTranTable1
set some_int =12where id =2;update dbo.TestTranTable1
set some_int =13where id =3;-- this will error out (arithmetic overflow)update dbo.TestTranTable1
set some_int =2147483648where id =4;
go
exec dbo.ChangeValues;select*from dbo.TestTranTable1;
Aqui está a saída:
Criando uma sessão de Eventos Estendidos para monitorar o sql_transactionevento, eis a saída da execução dbo.ChangeValues:
Como você pode ver nesta captura de tela acima, existem transações separadas para cada uma das quatro instruções. Os 3 primeiros confirmam e o último reverte por causa do erro.
Acho que pode haver alguma confusão aqui sobre um lote versus uma transação .
Uma transação é uma declaração ou conjunto de declarações que terão êxito ou falharão como uma unidade. Todas as instruções DDL estão nas próprias transações (ou seja, se você atualizar 100 linhas, mas a linha 98 gerar um erro, nenhuma das linhas será atualizada). Você também pode agrupar uma série de instruções em uma transação usando BEGIN TRANSACTIONe, em seguida, ou COMMITou ROLLBACK.
Um lote é uma série de instruções que são executadas juntas. Um procedimento armazenado é um exemplo de lote. Em um procedimento armazenado, se uma instrução falhar e houver interceptação de erro (normalmente TRY/CATCHbloqueia), as instruções subsequentes não serão executadas.
Suspeito que seu problema é que o lote está sendo cancelado quando ocorre um erro porque o próprio processo armazenado ou um escopo externo (como o aplicativo ou o processo armazenado que chama esse procedimento) possui um erro de interceptação. Se for esse o caso, isso é mais difícil de resolver, pois você precisa ajustar a maneira como lida com os erros em qualquer escopo que os esteja capturando.
Não encontrei nenhum artigo que diz: "Um procedimento de armazenamento é um exemplo de lote". Acredito que o procedimento armazenado é muito semelhante ao lote, mas não é um lote. A principal diferença é: o SP é garantido para ser compilado com antecedência e pronto para execução várias vezes, diferentemente dos lotes. As semelhanças são: - Ambos executam cada comando de cada vez. - Se um comando falhar, todos os comandos anteriores serão confirmados (a menos que estejam em execução em uma transação) - se um comando falhar, todos os comandos seguintes não serão executados.
Ashi
6
Tudo no servidor sql está contido em uma transação.
Quando você especifica explicitamente begin transactione end transactioné chamado de transação explícita . Quando você não faz, é uma transação implícita .
Para alternar em qual modo você está, use
set implicit_transactions on
ou
set implicit_transactions offselect@@OPTIONS &2
se acima retornar 2, você estará no modo de transação implícita. Se retornar 0, você estará na confirmação automática.
Uma transação é TUDO ou nada para manter o banco de dados em um estado consistente. Lembre-se das propriedades ACID.
É assim que os procedimentos armazenados funcionam por padrão. O procedimento armazenado não é quebrado automaticamente em uma transação.
Se você deseja que o procedimento armazenado pare quando ocorrer o primeiro erro, coloque algum login TRY / CATCH lá para retornar no caso de um problema com o comando 2, por exemplo.
Eu quero qualificar que transações individuais sejam o comportamento padrão para procedimentos armazenados, porque todas as instruções são agrupadas em transações implícitas; no entanto, ninguém deve confiar em transações implícitas para controlar o destino de seu código. É uma prática muito melhor controlar explicitamente a maneira como as transações são tratadas no código de produção.
separe cada uma das partes com um BEGIN TRAN e verifique se a transação foi bem-sucedida. se foi confirmado, caso contrário, faça uma reversão, uma vez que todos estão sendo executados no mesmo nível, você poderá confirmar cada seção separadamente sem precisar reverter tudo se houver uma falha.
Isso criará sub-transações dentro do meu Procedimento Armazenado? Idealmente, eu gostaria de evitar que se possível
Matthew Steeples
11
Se o SP for chamado de dentro de uma transação, as transações salvas acima serão a resposta. Se o sp não for chamado com, então @mrdenny está correto. Servidor SQL não suporta transação aninhada.
Para ser claro (e para os preguiçosos que não querem clicar nos links), na verdade você não está iniciando outra transação. Também chamado de título no post de Paul: "Mito: transações aninhadas são reais". Eles não são transações reais. COMMIT em uma transação aninhada não faz nada, exceto decremento @@ TRANCOUNT. É verdade que você não obtém um erro se aninhar BEGIN TRAN / COMMIT, mas isso é diferente de ter reais transições aninhadas.
Tudo no servidor sql está contido em uma transação.
Quando você especifica explicitamente
begin transaction
eend transaction
é chamado de transação explícita . Quando você não faz, é uma transação implícita .Para alternar em qual modo você está, use
ou
se acima retornar 2, você estará no modo de transação implícita. Se retornar 0, você estará na confirmação automática.
Uma transação é TUDO ou nada para manter o banco de dados em um estado consistente. Lembre-se das propriedades ACID.
- crie SP agora - observe que os três primeiros serão bem-sucedidos e o quarto falhará devido ao truncamento de strings ...
Consulte: É uma prática ruim sempre criar uma transação?
fonte
É assim que os procedimentos armazenados funcionam por padrão. O procedimento armazenado não é quebrado automaticamente em uma transação.
Se você deseja que o procedimento armazenado pare quando ocorrer o primeiro erro, coloque algum login TRY / CATCH lá para retornar no caso de um problema com o comando 2, por exemplo.
fonte
Você precisará de transações individuais para cada comando. Você também pode fazer isso com transações salvas:
Veja
SAVE TRANSACTION (Transact-SQL)
na documentação do produto.Eu quero qualificar que transações individuais sejam o comportamento padrão para procedimentos armazenados, porque todas as instruções são agrupadas em transações implícitas; no entanto, ninguém deve confiar em transações implícitas para controlar o destino de seu código. É uma prática muito melhor controlar explicitamente a maneira como as transações são tratadas no código de produção.
fonte
separe cada uma das partes com um BEGIN TRAN e verifique se a transação foi bem-sucedida. se foi confirmado, caso contrário, faça uma reversão, uma vez que todos estão sendo executados no mesmo nível, você poderá confirmar cada seção separadamente sem precisar reverter tudo se houver uma falha.
Para obter mais informações, consulte: http://msdn.microsoft.com/en-us/library/ms188929.aspx
fonte