XACT STATE: примеры (SQL)
XACT_STATE: smallintФункция XACT_STATE в MS SQL Server
Функция XACT_STATE() является скалярной функцией в Microsoft SQL Server, предназначенной для определения состояния текущей пользовательской транзакции в контексте выполнения. Она не принимает параметров и возвращает целочисленное значение, отражающее состояние транзакции в момент вызова.
Основное применение функции связано с обработкой ошибок в блоках TRY...CATCH для принятия решения о возможности фиксации транзакции или необходимости ее отката. Функция помогает идентифицировать, находится ли транзакция в работоспособном состоянии или в состоянии, которое требует обязательного отката.
Возвращаемые значения:
- 1 — транзакция активна и может быть успешно завершена (как COMMIT, так и ROLLBACK). Этот статус указывает на отсутствие фатальных ошибок, переводящих транзакцию в нефиксируемое состояние.
- 0 — отсутствует активная пользовательская транзакция для текущего сеанса. Это означает, что транзакция не была начата или была завершена (зафиксирована или откатана).
- -1 — транзакция активна, но находится в состоянии невозможности фиксации (uncommittable). Возникает при возникновении фатальной ошибки внутри транзакции. Единственное допустимое действие — выполнение полного отката транзакции (ROLLBACK).
Простые примеры использования
Пример демонстрирует поведение функции при отсутствии активной транзакции.
SELECT XACT_STATE() AS TransactionState;TransactionState ---------------- 0
Пример в контексте блока TRY...CATCH с фатальной ошибкой, переводящей транзакцию в состояние -1.
BEGIN TRY
BEGIN TRANSACTION;
-- Имитация фатальной ошибки: деление на ноль
SELECT 1/0;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
SELECT
XACT_STATE() AS XACT_State,
ERROR_NUMBER() AS ErrorNumber;
IF XACT_STATE() = -1
ROLLBACK TRANSACTION;
END CATCH;XACT_State ErrorNumber ---------- ----------- -1 8134
Пример успешной транзакции.
BEGIN TRY
BEGIN TRANSACTION;
PRINT 'Транзакция начата';
SELECT XACT_STATE() AS State_In_Try;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
SELECT XACT_STATE() AS State_In_Catch;
IF XACT_STATE() != 0
ROLLBACK TRANSACTION;
END CATCH;
-- После коммита
SELECT XACT_STATE() AS State_After_Commit;State_In_Try ------------- 1 State_After_Commit ------------------ 0
Похожие функции в MS SQL Server
@@TRANCOUNT — системная функция, возвращающая количество активных транзакций для текущего соединения. В отличие от XACT_STATE, она не различает фиксируемое и нефиксируемое состояние, а только показывает уровень вложенности транзакций. Используется для проверки, выполняется ли транзакция в данный момент.
Выбор между функциями: XACT_STATE применяют в обработчиках ошибок CATCH для точного определения допустимых действий над транзакцией. Функцию @@TRANCOUNT чаще используют для проверки наличия открытой транзакции перед началом новой или для управления уровнем вложенности.
Аналоги в других СУБД
PostgreSQL: Прямого аналога функции XACT_STATE не существует. Состояние транзакции контролируется через механизм исключений в блоках PL/pgSQL. Для проверки наличия активной транзакции можно использовать SELECT txid_current();, который вернет 0, если транзакции нет.
BEGIN;
SELECT txid_current() \gx
ROLLBACK;
SELECT txid_current() \gx- RECORD 1 --------- txid_current | 529 - RECORD 1 --------- txid_current | 0
Oracle: Аналогом можно считать DBMS_TRANSACTION.LOCAL_TRANSACTION_ID, которая возвращает NULL при отсутствии активной транзакции. Однако она не предоставляет информацию о фиксируемости.
MySQL: Отсутствует прямая замена. Состояние транзакции обычно определяется через обработчики в хранимых процедурах. Для проверки активности транзакции можно использовать переменную @@autocommit и статус команды.
Типичные ошибки
Попытка COMMIT при XACT_STATE = -1 приводит к ошибке, так как транзакция в нефиксируемом состоянии допускает только ROLLBACK.
BEGIN TRY
BEGIN TRANSACTION;
RAISERROR('Фатальная ошибка', 16, 1) WITH LOG;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF XACT_STATE() = -1
BEGIN
PRINT 'Состояние: ' + CAST(XACT_STATE() AS VARCHAR);
COMMIT TRANSACTION; -- Ошибка
END
END CATCH;Msg 3930, Level 16, State 1... Текущая транзакция не может быть зафиксирована... Ее следует откатить.
Игнорирование проверки XACT_STATE в блоке CATCH может привести к попытке выполнения операций над несуществующей или некорректной транзакцией.
BEGIN TRY
SELECT 1/0;
BEGIN TRANSACTION;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION; -- Ошибка, если транзакция не начата
END CATCH;Msg 3903, Level 16, State 1... Нет активной транзакции для ROLLBACK.
Изменения в последних версиях
Функция XACT STATE была введена в SQL Server 2005 и с тех пор ее поведение и возвращаемые значения остаются стабильными. Существенных изменений в последующих версиях SQL Server (включая 2012, 2014, 2016, 2017, 2019, 2022) не зафиксировано. Однако, всегда рекомендуется проверять документацию для конкретной версии, так как могут меняться контексты, в которых возникает состояние -1.
Расширенные примеры
Обработка вложенных транзакций. XACT_STATE отражает состояние самой внешней транзакции, а не уровень вложенности.
BEGIN TRANSACTION OutTran;
SELECT XACT_STATE() AS State1; -- 1
BEGIN TRY
SAVE TRANSACTION SavePoint;
SELECT XACT_STATE() AS State2; -- 1
RAISERROR('Ошибка', 16, 1);
END TRY
BEGIN CATCH
SELECT XACT_STATE() AS State3_In_Catch; -- 1 (не -1, так как ошибка не фатальная)
ROLLBACK TRANSACTION SavePoint;
SELECT XACT_STATE() AS State4_After_SavepointRollback; -- 1
END CATCH;
COMMIT TRANSACTION OutTran;State1 ------ 1 State2 ------ 1 State3_In_Catch --------------- 1 State4_After_SavepointRollback ------------------------------ 1
Использование с табличными переменными. Ошибки внутри транзакции, связанные с табличными переменными, обычно не переводят транзакцию в состояние -1.
BEGIN TRY
BEGIN TRANSACTION;
DECLARE @Test TABLE (id INT PRIMARY KEY);
INSERT INTO @Test VALUES (1);
INSERT INTO @Test VALUES (1); -- Нарушение первичного ключа
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
SELECT
XACT_STATE() AS XACT_State,
ERROR_NUMBER() AS ErrorNum;
IF XACT_STATE() = 1
COMMIT TRANSACTION; -- Возможно
ELSE IF XACT_STATE() = -1
ROLLBACK TRANSACTION;
END CATCH;XACT_State ErrorNum ---------- --------- 1 2627
Комбинация с @@TRANCOUNT для комплексной логики.
BEGIN TRY
BEGIN TRANSACTION;
SELECT
XACT_STATE() AS XACT_State,
@@TRANCOUNT AS TranCount;
-- Имитация фатальной ошибки
RAISERROR('Фатально', 20, 1) WITH LOG;
END TRY
BEGIN CATCH
SELECT
XACT_STATE() AS XACT_State_In_Catch,
@@TRANCOUNT AS TranCount_In_Catch;
IF XACT_STATE() = -1
PRINT 'Требуется полный откат';
END CATCH;XACT_State TranCount ---------- --------- 1 1 Сообщение об ошибке 2745... XACT_State_In_Catch TranCount_In_Catch ------------------- ------------------ -1 1