Uma transação em SQL é uma unidade de execução que agrupa uma ou mais tarefas. Uma transação é considerada bem-sucedida se todas as tarefas dentro dela forem executadas sem erros.
No entanto, se qualquer uma das tarefas em uma transação não for executada, toda a transação falhará. Uma transação tem apenas dois resultados:bem-sucedida ou com falha.
Um cenário prático
Considere um exemplo prático de um ATM (Automated Teller Machine). Você vai ao caixa eletrônico e ele pede seu cartão. Ele executa uma consulta para verificar se o cartão é válido ou não. Em seguida, ele solicita seu código PIN. Novamente, ele executa uma consulta para corresponder ao código PIN. O caixa eletrônico solicita o valor que você deseja sacar e você insere o valor desejado. O caixa eletrônico executa outra consulta para deduzir esse valor da sua conta e, em seguida, libera os fundos para você.
E se o valor for descontado da sua conta e o sistema travar devido a uma falha de energia sem dispensar as notas?
Isso é problemático porque o cliente tem os fundos deduzidos sem ter recebido nenhum dinheiro. É aqui que as transações podem ser úteis.
No caso de uma falha do sistema ou qualquer outro erro, todas as tarefas dentro da transação são revertidas. Portanto, no caso de um caixa eletrônico, o valor será adicionado de volta à sua conta se você não conseguir sacar por qualquer motivo.
O que é uma transação?
Em sua forma mais simples, uma mudança em uma tabela de banco de dados é uma transação. Portanto, as instruções INSERT, UPDATE e DELETE são todas instruções de transação. Quando você escreve uma consulta, uma transação é executada. No entanto, esta transação não pode ser revertida. Veremos como as transações são criadas, confirmadas e revertidas abaixo, mas primeiro vamos criar alguns dados fictícios para trabalhar.
Preparando os dados
Execute o script a seguir em seu servidor de banco de dados.
CREATE DATABASE schooldb CREATE TABLE student ( id INT PRIMARY KEY, name VARCHAR(50) NOT NULL, gender VARCHAR(50) NOT NULL, age INT NOT NULL, total_score INT NOT NULL, ) INSERT INTO student VALUES (1, 'Jolly', 'Female', 20, 500), (2, 'Jon', 'Male', 22, 545), (3, 'Sara', 'Female', 25, 600), (4, 'Laura', 'Female', 18, 400), (5, 'Alan', 'Male', 20, 500)
O script SQL acima cria um banco de dados schooldb. Nesse banco de dados, uma tabela student é criada e alguns dados fictícios são adicionados a essa tabela.
Executando consultas sem transações
Vamos executar três consultas padrão. Não estamos usando transações no momento.
INSERT INTO student VALUES (6, 'Suzi', 'Female', 25, 395) UPDATE student SET age = 'Six' WHERE id= 6 DELETE from student WHERE id = 6
Aqui a primeira consulta insere um registro de aluno no banco de dados. A segunda consulta atualiza a idade do aluno e a terceira consulta exclui o registro recém-inserido.
Se você executar o script acima, verá que o registro será inserido no banco de dados e então ocorrerá um erro ao executar a segunda consulta.
Se você observar a segunda consulta, estamos atualizando a idade armazenando um valor de string na coluna de idade que pode armazenar dados do tipo inteiro. Portanto, um erro será lançado. No entanto, a primeira consulta ainda será concluída com êxito. Isso significa que, se você selecionar todos os registros da tabela de alunos, verá o registro recém-inserido.
[ID da tabela=23 /]
Você pode ver que o registro com id=6 e nome ‘Suzi’ foi inserido no banco de dados. Mas a idade não pôde ser atualizada e a segunda consulta falhou.
E se não quisermos isso? E se quisermos ter certeza de que todas as consultas são executadas com êxito ou nenhuma das consultas é executada? É aqui que as transações são úteis.
Executando consultas com transações
Agora vamos executar as três consultas acima dentro de uma transação.
Primeiro, vamos ver como criar e confirmar uma transação.
Criando uma transação
Para executar uma consulta/consultas como uma transação, basta agrupar as consultas nas palavras-chave BEGIN TRANSACTION e COMMIT TRANSACTION. O BEGIN TRANSACTION declara o início de uma TRANSACTION enquanto o COMMIT TRANSACTION indica que a transação foi concluída.
Vamos executar três novas consultas no banco de dados que criamos anteriormente como uma transação. Estaremos adicionando um novo registro para um novo aluno com ID 7.
BEGIN TRANSACTION INSERT INTO student VALUES (7, 'Jena', 'Female', 22, 456) UPDATE student SET age = 'Twenty Three' WHERE id= 7 DELETE from student WHERE id = 7 COMMIT TRANSACTION
Quando a transação acima for executada, novamente ocorrerá um erro na segunda consulta, pois novamente um valor do tipo string está sendo armazenado na coluna age que armazena apenas dados do tipo inteiro.
No entanto, como o erro ocorre dentro de uma transação, todas as consultas executadas com sucesso antes da ocorrência desse erro serão automaticamente revertidas. Portanto, a primeira consulta que insere um novo registro de aluno com id =7 e nome ‘Jena’ também será revertida.
Agora, se você selecionar todos os registros da tabela de alunos, verá que o novo registro para ‘Jena’ não foi inserido.
Reversão de transação manual
Sabemos que se uma consulta gerar um erro em uma transação, toda a transação, incluindo todas as consultas já executadas, será automaticamente revertida. No entanto, também podemos reverter manualmente uma transação sempre que quisermos.
Para reverter uma transação, a palavra-chave ROLLBACK é usada seguida do nome da transação. Para nomear uma transação, a seguinte sintaxe é usada:
BEGIN TRANSACTION Transaction_name
Suponha que queremos que nossa tabela de alunos não tenha registros contendo nomes de alunos duplicados. Adicionaremos um registro para um novo aluno. Em seguida, verificaremos se existe no banco de dados um aluno com nome idêntico ao nome do aluno recém-inserido. Se o aluno com esse nome ainda não existir, confirmaremos nossa transação. Se existir um aluno com esse nome, reverteremos nossa transação. Faremos uso de declarações condicionais em nossa consulta.
Dê uma olhada na seguinte transação:
DECLARE @NameCount int BEGIN TRANSACTION AddStudent INSERT INTO student VALUES (8, 'Jacob', 'Male', 21, 600) SELECT @NameCount = COUNT(*) FROM student WHERE name = 'Jacob' IF @NameCount > 1 BEGIN ROLLBACK TRANSACTION AddStudent PRINT 'A student with this name already exists' END ELSE BEGIN COMMIT TRANSACTION AddStudent PRINT 'New record added successfully' END
Dê uma olhada cuidadosa no script acima. Muitas coisas estão acontecendo aqui.
Na primeira linha, criamos uma variável SQL do tipo inteiro NameCount.
Em seguida, iniciamos uma transação chamada 'AddStudent'. Você pode dar qualquer nome à sua transação.
Dentro da transação, inserimos um novo registro para um aluno com id =8 e nome ‘Jacob’.
A seguir, usando a função agregada COUNT contamos o número de registros de alunos onde o nome é ‘Jacob’ e armazenamos o resultado na variável ‘NameCount’.
Se o valor da variável for maior que 1, significa que um aluno com o nome ‘Jacob’ já existe no banco de dados. Nesse caso, ROLLBACK nossa transação e IMPRIMIR uma mensagem na tela que ‘Já existe um aluno com este nome’.
Caso contrário, confirmamos nossa transação e exibimos a mensagem 'Novo registro adicionado com sucesso'.
Quando você executar a transação acima pela primeira vez, não haverá um registro de aluno com o nome 'Jacob'. Portanto, a transação será confirmada e a seguinte mensagem será impressa:
Agora tente executar o seguinte script SQL no servidor:
DECLARE @NameCount int BEGIN TRANSACTION AddStudent INSERT INTO student VALUES (9, 'Jacob', 'Male', 22, 400) SELECT @NameCount = COUNT(*) FROM student WHERE name = 'Jacob' IF @NameCount > 1 BEGIN ROLLBACK TRANSACTION AddStudent PRINT 'A student with this name already exists' END ELSE BEGIN COMMIT TRANSACTION PRINT 'New record added successfully' END
Aqui novamente, estamos inserindo o registro do aluno com id =9 e nome ‘Jacob’. Como já existe um cadastro de aluno com o nome ‘Jacob’ no banco de dados, a transação será revertida e a seguinte mensagem será impressa:
Links úteis
- Aulas sobre transações SQL