@@TRANCOUNT: примеры (SQL)
@@TRANCOUNT: intОписание функции @@TRANCOUNT
@@TRANCOUNT — это системная функция в Microsoft SQL Server, которая возвращает количество активных транзакций для текущего соединения. Она не принимает аргументов и всегда возвращает целочисленное значение типа INT.
Функция используется для контроля вложенности транзакций. Каждый оператор BEGIN TRANSACTION увеличивает счетчик на единицу. Фиксация транзакции с помощью COMMIT уменьшает счетчик на единицу, а откат с помощью ROLLBACK откатывает все транзакции и устанавливает значение счетчика в ноль, если не указана точка сохранения.
Значение функции:
- 0: отсутствие активных транзакций для данного соединения.
- 1: одна активная транзакция (не вложенная).
- N: количество вложенных транзакций.
Функция часто применяется в хранимых процедурах для проверки, была ли транзакция начата внутри процедуры или вызвана извне, что помогает избежать лишних вложенных BEGIN TRANSACTION.
Краткие примеры использования
Пример базового использования для проверки наличия транзакции.
SELECT @@TRANCOUNT AS TransactionCount;TransactionCount 0
Изменение значения счетчика при начале и завершении транзакции.
BEGIN TRANSACTION;
SELECT @@TRANCOUNT AS AfterBegin; -- 1
COMMIT;
SELECT @@TRANCOUNT AS AfterCommit; -- 0AfterBegin 1 AfterCommit 0
Пример с вложенными транзакциями.
SELECT @@TRANCOUNT AS StartCount; -- 0
BEGIN TRANSACTION OuterTran;
SELECT @@TRANCOUNT AS AfterOuter; -- 1
BEGIN TRANSACTION InnerTran;
SELECT @@TRANCOUNT AS AfterInner; -- 2
COMMIT; -- Уменьшает счетчик до 1
SELECT @@TRANCOUNT AS AfterInnerCommit; -- 1
COMMIT; -- Уменьшает счетчик до 0
SELECT @@TRANCOUNT AS FinalCount; -- 0StartCount 0 AfterOuter 1 AfterInner 2 AfterInnerCommit 1 FinalCount 0
Похожие функции в MS SQL
В MS SQL Server прямой альтернативы для получения уровня вложенности транзакций нет. Однако для управления транзакциями используются другие конструкции:
- XACT_STATE(): Возвращает состояние транзакции текущего сеанса. Значения: 1 (активная, может быть зафиксирована), 0 (нет активной транзакции), -1 (активная, но не может быть зафиксирована из-за ошибки, требуется откат). Эта функция дополняет @@TRANCOUNT, показывая состояние, а не количество.
- @@ERROR и TRY...CATCH: Часто используются вместе с @@TRANCOUNT в блоках CATCH для принятия решения о фиксации или откате на основе наличия открытой транзакции.
- Точки сохранения (SAVE TRANSACTION): Позволяют выполнять частичный откат внутри транзакции, не влияя на значение @@TRANCOUNT.
@@TRANCOUNT предпочтительнее использовать для контроля вложенности, в то время как XACT_STATE() необходима для проверки возможности фиксации в случае ошибок.
Альтернативы в других СУБД
Концепция счетчика транзакций существует во многих СУБД, но реализация отличается.
PostgreSQL: Не имеет прямого аналога. Уровень вложенности транзакций можно узнать через подпроцедурные блоки в PL/pgSQL, используя переменные. Используются вложенные блоки SAVEPOINT.
-- В PostgreSQL нет @@TRANCOUNT
BEGIN;
-- Транзакция активна
SAVEPOINT my_savepoint;
-- Вложенная точка сохранения
ROLLBACK TO my_savepoint;
COMMIT;Oracle: Используется запрос к системному представлению V$TRANSACTION для проверки активных транзакций, но не для точного подсчета вложенности в текущем сеансе так, как в SQL Server. В PL/SQL можно использовать свой счетчик.
-- Проверка активной транзакции в Oracle
SELECT COUNT(*) FROM v$transaction WHERE ses_addr = SYS_CONTEXT('USERENV', 'SID');MySQL: Поддержка вложенных транзакций зависит от движка. В InnoDB есть SAVEPOINT. Прямого аналога @@TRANCOUNT нет, используется логика с переменными.
START TRANSACTION;
SAVEPOINT sp1;
-- Вложенная логика
ROLLBACK TO SAVEPOINT sp1;
COMMIT;SQLite: Не поддерживает вложенные транзакции на уровне одного соединения в классическом понимании. Последующие BEGIN игнорируются до COMMIT/ROLLBACK.
Типичные ошибки
1. Неправильная интерпретация значения. Значение 1 не всегда означает невложенную транзакцию — она может быть частью логической вложенности, если внешняя транзакция была зафиксирована.
BEGIN TRAN;
COMMIT; -- Счетчик стал 0
BEGIN TRAN; -- Снова 1, но это новая независимая транзакция
SELECT @@TRANCOUNT; -- Вернет 11
2. Пропуск проверки @@TRANCOUNT перед BEGIN TRANSACTION в хранимых процедурах, что приводит к излишней вложенности.
CREATE PROCEDURE MyProc AS
BEGIN
-- Ошибка: всегда начинает новую транзакцию
BEGIN TRANSACTION;
-- ... логика
COMMIT;
END;3. Использование ROLLBACK без условия, что сбрасывает счетчик в ноль, даже если транзакция была вложенной.
BEGIN TRAN T1; -- @@TRANCOUNT = 1
BEGIN TRAN T2; -- @@TRANCOUNT = 2
ROLLBACK; -- Откатывает ВСЕ, @@TRANCOUNT = 0
-- Транзакция T1 больше не активна4. Попытка COMMIT при @@TRANCOUNT = 0 вызывает ошибку.
COMMIT; -- Без начала транзакцииMsg 3902, Level 16, State 1 Команда COMMIT TRANSACTION не имеет соответствующей команды BEGIN TRANSACTION.
Изменения в последних версиях
Функция @@TRANCOUNT остается неизменной в своих базовых принципах работы на протяжении многих версий SQL Server (2005, 2008, 2012, 2014, 2016, 2017, 2019, 2022).
Косвенные изменения, связанные с транзакциями, которые могут влиять на контекст использования функции:
- SQL Server 2005: Улучшения в обработке ошибок с введением TRY...CATCH, что изменило типичные паттерны использования @@TRANCOUNT в блоках CATCH.
- SQL Server 2014: Ускоренные операции восстановления базы данных, которые влияют на время фиксации длинных транзакций, но не на логику @@TRANCOUNT.
- Azure SQL Database/Managed Instance: Полная поддержка функции, поведение идентично box-версии SQL Server.
Рекомендуется всегда использовать последнюю стабильную версию SQL Server для лучшей производительности и безопасности при работе с транзакциями.
Расширенные примеры
Пример использования в хранимой процедуре с проверкой существующей транзакции.
CREATE PROCEDURE UpdateData
@Param INT
AS
BEGIN
DECLARE @TranStarted BIT = 0;
IF @@TRANCOUNT = 0
BEGIN
BEGIN TRANSACTION;
SET @TranStarted = 1;
END
BEGIN TRY
UPDATE MyTable SET Col = @Param WHERE Id = 1;
IF @TranStarted = 1
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @TranStarted = 1
ROLLBACK TRANSACTION;
THROW;
END CATCH
END;Использование с XACT_STATE() в блоке CATCH для безопасного отката.
BEGIN TRY
BEGIN TRANSACTION;
DELETE FROM Orders WHERE OrderId = 100;
-- Имитация ошибки
RAISERROR('Искусственная ошибка', 16, 1);
COMMIT;
END TRY
BEGIN CATCH
SELECT
ERROR_MESSAGE() AS ErrorMsg,
@@TRANCOUNT AS TranCount,
XACT_STATE() AS XState;
IF XACT_STATE() = -1 OR @@TRANCOUNT > 0
ROLLBACK;
END CATCHErrorMsg TranCount XState Искусственная ошибка 1 -1
Работа с точками сохранения.
BEGIN TRANSACTION; -- @@TRANCOUNT = 1
SAVE TRANSACTION SavePoint1;
DELETE FROM TempData;
SELECT @@TRANCOUNT; -- Все еще 1
ROLLBACK TRANSACTION SavePoint1; -- Откат только удаления
COMMIT; -- Фиксация основной транзакции1
Пример логирования уровня транзакций в триггере.
CREATE TRIGGER MyTrigger ON MyTable
AFTER INSERT
AS
BEGIN
DECLARE @TranLevel INT = @@TRANCOUNT;
INSERT INTO AuditLog (Event, TranLevel, EventDate)
VALUES ('INSERT', @TranLevel, GETDATE());
END;