
Часть 2. Управление транзакциями
Под управлением транзакциями понимается способность управлять различными операциями над данными, которые выполняются внутри реляционной СУБД.
Прежде всего, имеется в виду выполнение операторов
INSERT,
UPDATE,
DELETE.
Например, после создания таблицы (выполнения оператора CREATE TABLE) не нужно фиксировать результат, поскольку создание таблицы фиксируется в базе данных автоматически.
Или, например, с помощью команды отмены транзакции не удастся восстановить только что удаленную оператором DROP TABLE таблицу.
Нужно иметь в виду, что после успешного выполнения отдельных команд, заключенных в тело одной транзакции, немедленного изменения данных не происходит.
После завершения транзакции вся информация о произведенных изменениях хранится либо в специально выделенной оперативной памяти, либо во временной области отката в самой базе данных до тех пор, пока не будет выполнена одна из команд управления транзакциями:
• COMMIT - для сохранения изменений;
• ROLLBACK - для отмены изменений;
• SAVEPOINT - для установки особых точек возврата.
Тогда все изменения или фиксируются в базе данных, или отбрасываются и временная область отката освобождается.
Команда COMMIT предназначена для сохранения в базе данных всех изменений, произошедших в ходе выполнения транзакции. Она сохраняет результаты всех операций, которые имели место после выполнения последней команды COMMIT или ROLLBACK.
Команда ROLLBACK предназначена для отмены транзакций, еще не сохраненных в базе данных. Она отменит только те транзакции, которые начались с момента выполнения последней команды COMMIT или ROLLBACK.
Команда SAVEPOINT (точка сохранения) предназначена для установки в транзакции особых точек, до которых в дальнейшем может быть произведен откат (при этом отката всей транзакции не происходит).
Команда имеет следующий вид:
SAVEPOINT [имя_точки_сохранения | @переменная точки сохранения]
Команда служит исключительно для создания точек сохранения среди операторов, ориентированных на изменение данных.
Имя точки сохранения в связанной с ней группе транзакций должно быть уникальным.
Для отмены действия группы транзакций, ограниченных точками сохранения, используется команда ROLLBACK со следующим синтаксисом:
ROLLBACK TO [имя_точки_сохранения | @переменная точки сохранения]
Поскольку с помощью команды SAVEPOINT крупная транзакция может быть разбита на меньшие, более управляемые группы, ее применение является одним из способов управления транзакциями.
Пример 1.
BEGIN TRAN
IF (SELECT COUNT(Поставки.Количество_ед_товара)
FROM Товары INNER JOIN Поставки
ON Товары. ID_товара = Поставки.Товар
WHERE Товары.Название_товара = ‘Кофе Ирландский крем’) >=400
BEGIN
ROLLBACK TRAN
RETURN
END
UPDATE Продажи
SET Цена_ед_товара = Цена_ед_товара + 500
FROM Товары INNER JOIN Продажи
ON Товары. ID_товара = Продажи.Товар
WHERE Товары.Название_товара = ‘Кофе Ирландский крем’
-- анализ кода ошибки производится при обновлении таблицы
IF @@error <>0
BEGIN
ROLLBACK TRAN
RETURN
END
COMMIT TRAN
В этом примере команда begin tran сообщает серверу о начале транзакции. Это означает, что до получения сервером команды завершения транзакции (commit tran) все изменения являются временными. Следовательно, если на сервере произойдет сбой после первого обновления, выполнится откат транзакции.
Термин "откат" означает, что все последствия транзакции будут отменены, и данные на сервере будут выглядеть так, словно никаких изменений не было.
Чтобы обеспечить согласованность транзакций, в этом примере блокируется модификация данных до завершения транзакции.
Термин "блокировка" (lock) означает, что никакой процесс не сможет обратиться к данным до их освобождения (посредством закрепления или отката транзакции). Только процесс, являющийся владельцем блокировки, сможет просмотреть заблокированные данные.
Если во время транзакции произойдут ошибки (или если этого требует логика программы), выполняется команда rollback tran.
Команды управления транзакциями не влияют на последовательность выполнения программы.
Примечание
Команда rollback tran не влияет на последовательность выполнения команд программы. Следовательно, после отката необходимо выполнить команду RETURN.
Пример 2. В этом примере иллюстрируется последовательность выполнения транзакций.
BEGIN TRAN Tr1
INSERT MyTable VALUES (1)
PRINT @@TRANCOUNT -- значение = 1
SAVE TRAN Point_1
INSERT MyTable VALUES (2) -- здесь может быть любой SQL-код
--Откат к точке Point_1, если возникла ошибка
IF @@error <> 0
ROLLBACK TRAN Point_1
ELSE
IF (SELECT COUNT(*) FROM MyTable) > 3
-- если меньше количество записей >3, произойдет откат к началу транзакции
ROLLBACK TRAN Tr1
INSERT MyTable VALUES (3) -- здесь может быть любой SQL-код
-- Откат к началу транзакции, если была хоть одна ошибка
IF @@error <> 0
ROLLBACK TRAN Tr1
COMMIT TRAN Tr1
Здесь после открытия транзакции добавляется запись в таблицу MyTable. Эта добавленная запись уже находится в таблице, но может участвовать в запросах только со стороны процесса, открывшего транзакцию. Она будет недоступна со стороны всех остальных процессов до закрепления транзакции.
Команда save tran используется в качестве "закладки". Она не влияет на значение @@trancount и не изменяет уровня вложенности транзакции, она лишь позволяет позднее вернуть транзакцию к этой точке.
Сервер запоминает факт начала транзакции, обновляя глобальную переменную @@trancount.
Вначале переменная @@trancount равна 0. Она увеличивается на 1 для каждой новой вложенной транзакции.
Управление транзакциями в среде MS SQL Server
В SQL Server транзакция определяется на уровне соединения с базой данных, а при закрытии соединения автоматически закрывается.
Когда транзакция начинается, все команды, выполненные в текущем соединении, считаются телом одной транзакции, пока не будет достигнут ее конец.
Пользователи в основном должны указывать только начало и конец транзакции, используя команды SQL или API (прикладного интерфейса программирования).
Если пользователь попытается установить соединение снова и продолжить выполнение транзакции, то это не удастся.
SQL Server поддерживает три вида определения транзакций:
• автоматическое;
• явное;
• неявное (подразумеваемое).
По умолчанию SQL Server работает в режиме автоматического начала транзакций, когда каждая команда рассматривается как отдельная транзакция.
При этом если отдельная команда выполнена успешно, то ее изменения фиксируются. Если при выполнении команды произошла ошибка, то сделанные изменения отменяются и система возвращается в первоначальное состояние.
Если пользователю необходимо создать транзакцию, включающую несколько команд, он должен явно указать транзакцию (режим явного определения).
Сам сервер обычно работает в одном из двух режимов определения транзакций - автоматическом или неявном. Но режим явного определения транзакций доминирует.
Установка режима явного определения транзакций выполняется посредством другой команды:
SET IMPLICIT_TRANSACTIONS ON
Для установки режима автоматического определения транзакций отключается режим явного определения транзакций - используется команда:
SET IMPLICIT_TRANSACTIONS OFF
Явные транзакции
Явные транзакции требуют, чтобы пользователь указал начало и конец транзакции, используя следующие команды:
Начало транзакции определяется командой
BEGIN TRAN [SACTION] [имя_транзакции | @имя_переменной_транзакции ]
[WITH MARK ['описание_транзакции']]
При этом в журнале транзакций фиксируется момент начала транзакции и первоначальные значения данных;
Конец транзакции. Если в теле транзакции не было ошибок, то следующая команда предписывает серверу зафиксировать все изменения, сделанные в транзакции, после чего в журнале транзакций помечается, что изменения зафиксированы и транзакция завершена:
COMMIT [TRANSACTION] [имя_транзакции | @имя_переменной_транзакции]
Внутри транзакции можно создавать точки сохранения. При выполнении следующей команды СУБД сохраняет состояние БД в текущей точке и присваивает этому состоянию имя точки сохранения:
SAVE TRAN [ SACTION ] [имя_точки_сохранения | @имя_переменной_точки_сохранения]
Прерывание транзакции. Когда сервер встречает приведенную ниже команду, происходит откат транзакции, восстанавливается первоначальное состояние системы и в журнале транзакций отмечается, что транзакция была отменена.
ROLLBACK [TRAN [ SACTION ]] [имя_транзакции | @имя_
переменной_транзакции | имя_точки__сохранеия | @имя_переменной_точки_сохранения]
Эта команда либо отменяет все изменения, сделанные в БД после оператора BEGIN TRANSACTION, либо отменяет изменения, сделанные в БД после точки сохранения, возвращая транзакцию к месту, где был выполнен оператор SAVE TRANSACTION.
Некоторые полезные функции:
Функция @@TRANCOUNT возвращает количество активных транзакций.
Функция @@NESTLEVEL возвращает уровень вложенности транзакций.
Пример 3. Использование точек сохранения.
Пусть применительно к таблице Товар выполняются следующие команды:
BEGIN TRAN
SAVE TRAN point1
При этом в точке point 1 сохраняется первоначальное состояние таблицы Товар
DELETE FROM Товар WHERE ID_Товара=2
SAVE TRAN point2
В точке point2 сохраняется состояние таблицы Товар без товара с кодом 2.
DELETE FROM Товар WHERE ID_Товара=3
SAVE TRAN point3
В точке point3 сохраняется состояние таблицы Товар без двух товаров - с кодом 2 и с кодом 3.
DELETE FROM Товар WHERE ID_Товара<>1
ROLLBACK TRAN point3
Сначала удаляются все записи кроме одной – с ID_Товара<>1. Но потом происходит отмена удаления и возврат в состояние таблицы без товаров с кодами 2 и 3.
SELECT * FROM Товар
Оператор SELECT покажет таблицу товар без товаров с кодами 2 и 3.
ROLLBACK TRAN point1
Происходит возврат в первоначальное состояние таблицы.
SELECT * FROM Товар
COMMIT
При этом сохраняется первоначальное состояние таблицы.
в) Вложенные транзакции
Вложенными называются транзакции, выполнение которых инициируется из тела уже активной транзакции.
Для создания вложенной транзакции пользователю не нужны какие-либо дополнительные команды. Он просто начинает новую транзакцию, не закрыв предыдущую. Завершение транзакции верхнего уровня откладывается до завершения вложенных транзакций.
Если транзакция самого нижнего вложенного уровня завершена неудачно и отменена, то все транзакции верхнего уровня, включая транзакцию первого уровня, будут отменены.
Кроме того, если несколько транзакций нижнего уровня были завершены успешно (но не зафиксированы), однако на среднем уровне (не самая верхняя транзакция) неудачно завершилась другая транзакция, то в соответствии с требованиями ACID произойдет откат всех транзакций всех уровней, включая успешно завершенные.
Только когда все транзакции на всех уровнях завершены успешно, происходит фиксация всех сделанных изменений.
Каждая команда COMMIT TRANSACTION работает только с последней начатой транзакцией. Даже если в команде COMMIT TRANSACTION указано имя транзакции более высокого уровня, будет завершена транзакция, начатая последней.
Если команда ROLLBACK TRANSACTION используется (на любом уровне вложенности) без указания имени транзакции, то откатываются все вложенные транзакции, включая транзакцию самого высокого (верхнего) уровня.
В команде ROLLBACK TRANSACTION разрешается указывать только имя самой верхней транзакции. Имена любых вложенных транзакций игнорируются, и попытка их указания приведет к ошибке.
Таким образом, при откате транзакции любого уровня вложенности всегда происходит откат всех транзакций.
Если же требуется откатить лишь часть транзакций, можно использовать команду SAVE TRANSACTION, с помощью которой создается точка сохранения.
Пример 2. Использование вложенных транзакций
BEGIN TRAN
INSERT Товар (Название_товара, категория) VALUES ('кефир’,40)
BEGIN TRAN
INSERT Товар (Название_товара, категория) VALUES ('ряженка', 40)
BEGIN TRAN
INSERT Товар (Название_товара, категория) VALUES {'йогурт', 40)
ROLLBACK TRAN
Здесь происходит возврат на начальное состояние таблицы, поскольку выполнение команды ROLLBACK TRAN без указания имени транзакции откатывает все транзакции.