Ошибки времени компиляции и выполнения
Иногда в режиме автоматической фиксации экземпляр компонента Database Engine откатывает назад весь пакет вместо одной инструкции SQL. Это происходит, если ошибка возникает во время компиляции, а не во время выполнения. Ошибка компиляции не позволяет компоненту Database Engine построить план выполнения, поэтому пакет не выполняется. Поскольку произошел откат назад всех инструкций, предшествующих неправильной инструкции, нельзя выполнить весь пакет.
В следующем примере ни одна из инструкций INSERT в третьем пакете не выполнится из-за ошибки компиляции. При этом произойдет откат первых двух инструкций INSERT и они не будут выполняться ни при каких условиях.
CREATE TABLE TestBatch (Cola INT PRIMARY KEY, Colb CHAR(3));
GO
INSERT INTO TestBatch VALUES (1, 'aaa');
INSERT INTO TestBatch VALUES (2, 'bbb');
INSERT INTO TestBatch VALUSE (3, 'ccc'); -- Синтаксическая ошибка.
GO
SELECT * FROM TestBatch; -- Не возвращает ни одной строки.
GO
В следующем примере третья инструкция INSERT вызывает ошибку повторения первичного ключа во время выполнения. Первые две инструкции INSERT выполняются успешно и фиксируются, поэтому остаются после возникновения ошибки времени выполнения.
CREATE TABLE TestBatch (Cola INT PRIMARY KEY, Colb CHAR(3));
GO
INSERT INTO TestBatch VALUES (1, 'aaa');
INSERT INTO TestBatch VALUES (2, 'bbb');
INSERT INTO TestBatch VALUES (1, 'ccc'); -- Дублирование первичного ключа.
GO
SELECT * FROM TestBatch; -- Возвращает строки 1 и 2.
GO
Компонент Database Engine использует отложенное разрешение имен, при котором имена объектов разрешаются только во время выполнения. В следующем примере первые две инструкции INSERT выполняются и фиксируются, а вставленные строки остаются в таблице TestBatch после того, как третья инструкция INSERT вызывает ошибку времени выполнения, ссылаясь на таблицу, которой не существует.
CREATE TABLE TestBatch (Cola INT PRIMARY KEY, Colb CHAR(3));
GO
INSERT INTO TestBatch VALUES (1, 'aaa');
INSERT INTO TestBatch VALUES (2, 'bbb');
INSERT INTO TestBch VALUES (3, 'ccc'); -- Неверное имя таблицы.
GO
SELECT * FROM TestBatch; -- Возвращает строки 1 и 2.
GO
Явные транзакции
Явными транзакциями являются транзакции, для которых явно назначаются запуск и остановка. Сценарии Transact-SQL используют для определения явных транзакций инструкции Transact-SQL BEGIN TRANSACTION, COMMIT TRANSACTION, COMMIT WORK, ROLLBACK TRANSACTION и ROLLBACK WORK.
BEGIN TRANSACTION
Отмечает точку запуска явной транзакции для соединения.
COMMIT TRANSACTION или COMMIT WORK
Используется для успешного завершения транзакции, если не было ошибок. Все изменения данных, сделанные в транзакции, становятся постоянной частью базы данных. Ресурсы, заблокированные транзакцией, высвобождаются.
ROLLBACK TRANSACTION или ROLLBACK WORK
Используется для удаления транзакции, если были ошибки. Все измененные транзакцией данные возвращаются в то состояние, в котором они были в момент запуска транзакции. Ресурсы, заблокированные транзакцией, высвобождаются.
Режим явной транзакции работает только во время выполнения транзакции. После завершения транзакции соединение возвращается в тот режим транзакции, в котором оно было до запуска явной транзакции, либо в неявный режим, либо в режим автоматической фиксации.
Неявные транзакции
Если соединение выполняется в режиме неявных транзакций, экземпляр компонента SQL Server Database Engine автоматически начинает новую транзакцию после фиксации или отката текущей. Для запуска таких транзакций ничего делать не нужно - необходимо только фиксировать или выполнять откат каждой транзакции. Режим неявных транзакций формирует непрерывную цепь транзакций.
После установления на соединении режима неявных транзакций экземпляр компонента Database Engine автоматически запускает транзакцию, если вначале выполняет любую из следующих инструкций:
ALTER TABLE | INSERT |
CREATE | OPEN |
DELETE | REVOKE |
DROP | SELECT |
FETCH | TRUNCATE TABLE |
GRANT | UPDATE |
Транзакция продолжает оставаться активной до тех пор, пока не будет выдана инструкция COMMIT или ROLLBACK. После фиксации или отката первой транзакции экземпляр компонента Database Engine автоматически запускает новую транзакцию каждый раз, когда на соединении выполняется какая-либо из этих инструкций. Экземпляр продолжает формировать цепь неявных транзакций до тех пор, пока не будет выключен режим неявных транзакций.
Режим неявных транзакций устанавливается или при помощи инструкции языка Transact-SQL SET, или функциями и методами API базы данных.
Инструкция SET IMPLICIT_TRANSACTIONS OFF используется для отключения режима неявных транзакций. Инструкции COMMIT TRANSACTION, COMMIT WORK, ROLLBACK TRANSACTION или ROLLBACK WORK используются для завершения каждой транзакции.
CREATE TABLE ImplicitTran (ColA int PRIMARY KEY, ColB char(3) NOT NULL);
GO
SET IMPLICIT_TRANSACTIONS ON;
GO
-- первая неявная транзакции инициируется командой INSERT.
INSERT INTO ImplicitTran VALUES (1, 'aaa');
GO
INSERT INTO ImplicitTran VALUES (2, 'bbb');
GO
-- фиксация первой транзакции.
COMMIT TRANSACTION;
GO
-- вторая неявная транзакции инициируется командой SELECT.
SELECT COUNT(*) FROM ImplicitTran;
GO
INSERT INTO ImplicitTran VALUES (3, 'ccc');
GO
SELECT * FROM ImplicitTran;
GO
-- фиксация второй транзакции.
COMMIT TRANSACTION;
GO
SET IMPLICIT_TRANSACTIONS OFF;
GO
Распределенные транзакции
Распределенные транзакции выполняются на двух или более серверах, которые называются диспетчерами ресурсов. Управление транзакцией должно координироваться между диспетчерами ресурсов компонентом сервера, который называется диспетчером транзакций. Каждый экземпляр компонента SQL Server Database Engine может действовать как диспетчер ресурсов в распределенных транзакциях, координируемых диспетчерами транзакций, например, координатором распределенных транзакций (MS DTC) или другими диспетчерами транзакций, поддерживающими спецификацию Open Group XA обработки распределенных транзакций.
Транзакция в отдельном экземпляре компонента Database Engine, распространяющаяся на несколько источников данных, в действительности является распределенной транзакцией. Экземпляр управляет распределенной транзакцией на внутреннем уровне, для пользователя она действует как локальная транзакция.
В приложении управление распределенной транзакцией во многом похоже на управление локальной. В конце транзакции приложение запрашивает ее фиксацию или откат. Распределенной фиксацией диспетчер транзакций должен управлять иначе, чтобы свести к минимуму риск сбоя сети, в результате которого одни диспетчеры ресурсов могут фиксировать транзакцию, тогда как другие будут выполнять ее откат. Выход из положения заключается в двухфазном процессе фиксации (фаза подготовки и фаза фиксации), который называется двухфазной фиксацией (2PC).
· Фаза подготовки
Когда диспетчер транзакции получает запрос на фиксацию, он отправляет команду подготовки всем диспетчерам ресурсов, занятым в транзакции. Каждый диспетчер ресурсов всемерно обеспечивает устойчивость транзакции, а все буферы, в которых хранятся образы журналов для этой транзакции, записываются на диск. По мере того, как каждый диспетчер ресурсов завершает фазу подготовки, он возвращает диспетчеру транзакций значение успешного или неуспешного завершения подготовки.
· Фаза фиксации
Если диспетчер транзакций получает значения успешного завершения подготовки от всех диспетчеров ресурсов, то он отправляет команду фиксации каждому диспетчеру ресурсов. После этого диспетчеры ресурсов могут завершить фиксацию. Если все диспетчеры ресурсов сообщают об успешной фиксации, то диспетчер транзакций отправляет уведомление приложению. Если какой-либо диспетчер ресурсов сообщил о неуспешном завершении подготовки, то диспетчер транзакций отправляет команду отката всем диспетчерам ресурсов и сообщает приложению о сбое фиксации.