Транзакцията в SQL е единица за изпълнение, която групира една или повече задачи заедно. Транзакцията се счита за успешна, ако всички задачи в нея се изпълняват без грешка.
Въпреки това, ако някоя от задачите в дадена транзакция не успее да се изпълни, цялата транзакция се проваля. Една транзакция има само два резултата:успешна или неуспешна.
Практичен сценарий
Помислете за практически пример за банкомат (автоматичен банкомат). Отивате до банкомата и той ви иска картата. Той изпълнява заявка, за да провери дали картата е валидна или не. След това ви иска вашия ПИН код. Отново изпълнява заявка за съвпадение с пин кода. След това банкоматът ви пита за сумата, която искате да изтеглите и вие въвеждате сумата, която искате. Банкоматът изпълнява друго запитване, за да приспадне тази сума от сметката ви и след това ви изплаща средствата.
Ами ако сумата бъде удържана от сметката ви и след това системата се срине поради прекъсване на захранването, без да се издават бележките?
Това е проблематично, защото на клиента са приспаднати средствата, без да е получил пари. Това е мястото, където транзакциите могат да бъдат полезни.
В случай на системен срив или друга грешка, всички задачи в транзакцията се връщат обратно. Следователно, в случай на банкомат, сумата ще бъде добавена обратно към вашата сметка, ако не можете да я изтеглите по някаква причина.
Какво е транзакция?
Най-просто, промяната в таблица на база данни е транзакция. Следователно операторите INSERT, UPDATE и DELETE са всички оператори за транзакции. Когато пишете заявка, се извършва транзакция. Тази транзакция обаче не може да бъде отменена. По-долу ще видим как се създават, извършват и връщат транзакциите, но първо нека създадем някои фиктивни данни, с които да работим.
Подготовка на данните
Изпълнете следния скрипт на вашия сървър на база данни.
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)
Горещият SQL скрипт създава schooldb база данни. В тази база данни се създава ученик на таблица и към тази таблица се добавят някои фиктивни данни.
Изпълнение на заявки без транзакции
Нека изпълним три стандартни заявки. В момента не използваме транзакции.
INSERT INTO student VALUES (6, 'Suzi', 'Female', 25, 395) UPDATE student SET age = 'Six' WHERE id= 6 DELETE from student WHERE id = 6
Тук първата заявка вмъква запис на ученик в базата данни. Втората заявка актуализира възрастта на ученика, а третата заявка изтрива нововмъкнатия запис.
Ако изпълните горния скрипт, ще видите, че записът ще бъде вмъкнат в базата данни и след това ще възникне грешка при изпълнение на втората заявка.
Ако погледнете втората заявка, ние актуализираме възрастта, като съхраняваме стойност на низ в колоната за възраст, която може да съхранява данни от целочислен тип. Следователно ще бъде изведена грешка. Първата заявка обаче ще завърши успешно. Това означава, че ако изберете всички записи от таблицата на учениците, ще видите нововмъкнат запис.
[table id=23 /]
Можете да видите, че записът с id=6 и име „Suzi“ е вмъкнат в базата данни. Но възрастта не можа да бъде актуализирана и втората заявка се провали.
Ами ако не искаме това? Ами ако искаме да сме сигурни, че или всички заявки се изпълняват успешно, или нито една от заявките не се изпълнява изобщо? Тук транзакциите са полезни.
Изпълнение на заявки с транзакции
Сега нека изпълним трите заявки по-горе в рамките на транзакция.
Първо, нека да видим как да създадем и извършим транзакция.
Създаване на транзакция
За да стартирате заявка/заявки като транзакция, просто увийте заявките в ключовите думи BEGIN TRANSACTION и COMMIT TRANSACTION. BEGIN TRANSACTION декларира началото на ТРАНЗАКЦИЯ, докато COMMIT TRANSACTION заявява, че транзакцията е завършена.
Нека изпълним три нови заявки към базата данни, която създадохме по-рано като транзакция. Ще добавим нов запис за нов ученик с 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
Когато се изпълни горната транзакция, отново ще възникне грешка във втората заявка, тъй като отново стойността на низовия тип се съхранява в колоната за възраст, която съхранява само данни от целочислен тип.
Въпреки това, тъй като грешката възниква в транзакция, всички заявки, които са били изпълнени успешно, преди да е възникнала тази грешка, автоматично ще бъдат върнати обратно. Следователно първата заявка, която вмъква нов запис на ученик с идентификатор =7 и име „Jena“, също ще бъде върната назад.
Сега, ако изберете всички записи от студентската таблица, ще видите, че новият запис за „Jena“ не е вмъкнат.
Отмяна на ръчна транзакция
Знаем, че ако заявка доведе до грешка в транзакция, цялата транзакция, включително всички вече изпълнени заявки, се връщат автоматично. Въпреки това, ние също можем ръчно да върнем транзакция ръчно, когато пожелаем.
За връщане на транзакция се използва ключовата дума ROLLBACK, последвана от името на транзакцията. За именуване на транзакция се използва следният синтаксис:
BEGIN TRANSACTION Transaction_name
Да предположим, че искаме нашата таблица за студенти да няма записи, съдържащи дублиращи се имена на ученици. Ще добавим запис за нов ученик. След това ще проверим дали в базата данни съществува ученик с име, идентично с името на нововмъкнатия ученик. Ако ученикът с това име все още не съществува, ние ще извършим транзакцията си. Ако студент с това име наистина съществува, ние ще отменим транзакцията си. Ще използваме условни изрази в нашата заявка.
Разгледайте следната транзакция:
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
Разгледайте внимателно горния скрипт. Тук се случват много неща.
В първия ред създаваме SQL променлива от целочислен тип NameCount.
След това започваме транзакция, наречена „AddStudent“. Можете да дадете произволно име на вашата транзакция.
Вътре в транзакцията вмъкнахме нов запис за ученик с идентификатор =8 и име „Джейкъб“.
След това, използвайки обобщената функция COUNT, преброяваме броя на студентските записи, където името е „Jacob“ и съхраняваме резултата в променлива „NameCount“.
Ако стойността на променливата е по-голяма от 1, това означава, че ученик с името „Jacob“ вече съществува в базата данни. В този случай връщаме нашата транзакция и ОТПЕЧАТВАМЕ съобщение на екрана, че „Студент с това име вече съществува“.
Ако не, ние извършваме транзакцията си и показваме съобщението „Нов запис е добавен успешно“.
Когато стартирате горната транзакция за първи път, няма да има запис на ученик с името „Jacob“. Следователно транзакцията ще бъде извършена и ще бъде отпечатано следното съобщение:
Сега опитайте да изпълните следния SQL скрипт на сървъра:
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
Тук отново вмъкваме запис на студент с идентификатор =9 и име „Jacob“. Тъй като запис на ученик с името „Jacob“ вече съществува в базата данни, транзакцията ще се върне назад и ще бъде отпечатано следното съобщение:
Полезни връзки
- Класове по SQL транзакции