Поделиться через


Вложенность транзакций

Явные транзакции могут быть вложенными. Обычно это используется для поддержки транзакций в хранимых процедурах, которые могут быть вызваны, или из процесса, который уже находится в транзакции, или из процесса, у которого нет активной транзакции.

В следующем примере показано намеренное использование вложенных транзакций. Процедура TransProc принудительно создает свою транзакцию, независимо от режима транзакции любого процесса, который ее выполняет. Если процедура TransProc вызывается при активной транзакции, вложенная транзакция в TransProc в значительной степени не учитываются, и ее инструкции INSERT фиксируются или откатываются, в зависимости от окончательного действия внешней транзакции. Если процедура TransProc выполняется процессом, не выполняющим отдельную транзакцию, инструкция COMMIT TRANSACTION в конце процедуры фактически фиксирует инструкции INSERT.

SET QUOTED_IDENTIFIER OFF;
GO
SET NOCOUNT OFF;
GO
USE AdventureWorks2008R2;
GO
CREATE TABLE TestTrans(Cola INT PRIMARY KEY,
               Colb CHAR(3) NOT NULL);
GO
CREATE PROCEDURE TransProc @PriKey INT, @CharCol CHAR(3) AS
BEGIN TRANSACTION InProc
INSERT INTO TestTrans VALUES (@PriKey, @CharCol)
INSERT INTO TestTrans VALUES (@PriKey + 1, @CharCol)
COMMIT TRANSACTION InProc;
GO
/* Start a transaction and execute TransProc. */
BEGIN TRANSACTION OutOfProc;
GO
EXEC TransProc 1, 'aaa';
GO
/* Roll back the outer transaction, this will
   roll back TransProc's nested transaction. */
ROLLBACK TRANSACTION OutOfProc;
GO
EXECUTE TransProc 3,'bbb';
GO
/* The following SELECT statement shows only rows 3 and 4 are 
   still in the table. This indicates that the commit
   of the inner transaction from the first EXECUTE statement of
   TransProc was overridden by the subsequent rollback. */
SELECT * FROM TestTrans;
GO

Фиксация внутренних транзакций не учитываются компонентом SQL Server Database Engine. Транзакция либо фиксируется, либо откатывается в зависимости от предпринятого действия в конце самой внешней транзакции. Если внешняя транзакция зафиксирована, внутренние вложенные транзакции также будут зафиксированы. Если внешняя транзакция откатывается, то все внутренние транзакции также будут отменены, независимо от того, были ли отдельные внутренние транзакции зафиксированы.

Каждый вызов инструкций COMMIT TRANSACTION или COMMIT WORK применяется к последней выполненной инструкции BEGIN TRANSACTION. Если инструкции BEGIN TRANSACTION являются вложенными, то инструкция COMMIT применяется только к последней вложенной транзакции, которая является самой внутренней транзакцией. Даже если инструкция COMMIT TRANSACTION transaction_name с вложенной транзакцией ссылается на имя внешней транзакции, фиксация происходит только в самой внутренней транзакции.

Нельзя ссылаться на внутренние транзакции набора именованных вложенных транзакций для аргумента transaction_name инструкции ROLLBACK TRANSACTION. Аргумент transaction_name может ссылаться только на имя самой внешней транзакции. Если инструкция ROLLBACK TRANSACTION transaction_name с именем самой внешней транзакции выполняется на любом уровне набора вложенных транзакций, для всех вложенных транзакций будет выполнен откат. Если инструкции ROLLBACK WORK или ROLLBACK TRANSACTION выполняются без аргумента transaction_name на любом уровне набора вложенных транзакций, происходит откат всех вложенных транзакций, включая самую внешнюю транзакцию.

Функция @@TRANCOUNT записывает текущий уровень вложенности транзакции. Каждая инструкция BEGIN TRANSACTION увеличивает значение @@TRANCOUNT на один. Каждая инструкция COMMIT TRANSACTION или COMMIT WORK уменьшает значение @@TRANCOUNT на один. Инструкция ROLLBACK WORK или ROLLBACK TRANSACTION, не содержащая имени транзакции, выполняет откат всех вложенных транзакций и уменьшает значение @@TRANCOUNT до 0. Инструкция ROLLBACK TRANSACTION, использующая имя самой внешней транзакции в наборе вложенных транзакций, откатывает все вложенные транзакции и уменьшает значение @@TRANCOUNT до 0. Чтобы определить, открыта ли транзакция, выполните инструкцию SELECT @@TRANCOUNT и посмотрите, возвращает ли она значение, которое больше или равно 1. Если значение @@TRANCOUNT равно 0, транзакции нет.