Desenvolvo o T-SQL há vários anos e estou sempre aprofundando, continuando a aprender tudo o que posso sobre todos os aspectos da linguagem. Recentemente, comecei a trabalhar em uma nova empresa e recebi o que acho uma sugestão estranha sobre transações. Nunca os use. Em vez disso, use uma solução alternativa que simule uma transação. Isso vem do nosso DBA, que trabalha em um banco de dados com muitas transações e, posteriormente, muito bloqueio. O banco de dados em que trabalho principalmente não sofre com esse problema e vejo que as transações foram usadas no passado.
Eu entendo que o bloqueio é esperado nas transações, pois é da natureza delas e se você pode fugir sem usar uma, faça isso de qualquer maneira. Mas tenho muitas ocasiões em que cada instrução DEVE executar com sucesso. Se alguém falha, todos devem falhar em se comprometer.
Sempre mantive o escopo de minhas transações o mais restrito possível, sempre usado em conjunto com SET XACT_ABORT ON e sempre dentro de um TRY / CATCH.
Exemplo:
CREATE SCHEMA someschema;
GO
CREATE TABLE someschema.tableA
(id INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
ColA VARCHAR(10) NOT NULL
);
GO
CREATE TABLE someschema.tableB
(id INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
ColB VARCHAR(10) NOT NULL
);
GO
CREATE PROCEDURE someschema.ProcedureName @ColA VARCHAR(10),
@ColB VARCHAR(10)
AS
SET NOCOUNT, XACT_ABORT ON;
BEGIN
BEGIN TRY
BEGIN TRANSACTION;
INSERT INTO someschema.tableA(ColA)
VALUES(@ColA);
INSERT INTO someschema.tableB(ColB)
VALUES(@ColB);
--Implement error
SELECT 1/0
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@trancount > 0
BEGIN
ROLLBACK TRANSACTION;
END;
THROW;
RETURN;
END CATCH;
END;
GO
Aqui está o que eles sugeriram que eu faça.
GO
CREATE PROCEDURE someschema.ProcedureNameNoTransaction @ColA VARCHAR(10),
@ColB VARCHAR(10)
AS
SET NOCOUNT ON;
BEGIN
BEGIN TRY
DECLARE @tableAid INT;
DECLARE @tableBid INT;
INSERT INTO someschema.tableA(ColA)
VALUES(@ColA);
SET @tableAid = SCOPE_IDENTITY();
INSERT INTO someschema.tableB(ColB)
VALUES(@ColB);
SET @tableBid = SCOPE_IDENTITY();
--Implement error
SELECT 1/0
END TRY
BEGIN CATCH
DELETE FROM someschema.tableA
WHERE id = @tableAid;
DELETE FROM someschema.tableB
WHERE id = @tableBid;
THROW;
RETURN;
END CATCH;
END;
GO
Minha pergunta para a comunidade é a seguinte. Isso faz sentido como uma solução alternativa viável para transações?
Minha opinião sobre o que sei sobre transações e o que a solução está propondo é que não, essa não é uma solução viável e apresenta muitos pontos de falha.
Na solução sugerida, vejo quatro transações implícitas ocorrendo. As duas inserções na tentativa e depois mais duas transações para as exclusões na captura. Ele "desfaz" as inserções, mas sem reverter nada, para que nada seja revertido.
Este é um exemplo muito básico para demonstrar o conceito que eles estão sugerindo. Alguns dos procedimentos armazenados reais nos quais eu venho fazendo isso os tornam exaustivamente longos e difíceis de gerenciar porque "reverter" vários conjuntos de resultados versus dois valores de parâmetro neste exemplo se torna bastante complicado, como você pode imaginar. Como "retroceder" está sendo feito manualmente agora, a oportunidade de perder algo porque é real.
Outro problema que acho que existe é para tempos limite ou conexões interrompidas. Isso ainda é revertido? Este é o meu entendimento de por que SET XACT_ABORT ON deve ser usado para que, nesses casos, a transação seja revertida.
Agradecemos desde já o seu feedback!