XACT STATE: примеры (SQL)

Руководство по применению XACT_STATE в SQL Server
Раздел: Функции обработки исключений, Транзакции
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 отражает состояние самой внешней транзакции, а не уровень вложенности.

Пример sql
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.

Пример sql
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 для комплексной логики.

Пример sql
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

MS SQL XACT_STATE function comments

En
XACT STATE Returns the user transaction state of a current running request