SET XACT_ABORT ON Игнорируется, транзакция продолжается (SQL Server 2008 R2)

Возможно, я что-то упускаю, но хотя RAISERRORs ниже имеют серьезность 16 (согласно документации), транзакция по-прежнему совершается, как если бы XACT_ABORT ON не имеет никакого эффекта

CREATE PROCEDURE [ExploringGroups].[RemoveMember]
@groupId        uniqueidentifier,
@adminUsername  nvarchar(50),
@targetUsername nvarchar(50)
AS
SET XACT_ABORT ON

BEGIN TRANSACTION

DECLARE
    @adminUserId    uniqueidentifier = dbo.fn_userId(@adminUsername),
    @targetUserId   uniqueidentifier = dbo.fn_userId(@targetUsername)

IF @targetUserId IS NULL OR ExploringGroups.IsMember(@groupId, @targetUserId) = 0
    RAISERROR('Target user was not located', 16, 1)

IF ExploringGroups.IsInRole(@groupId, @adminUserId, 'adm') = 0
    RAISERROR('Specified user is not an administrator of this group', 16, 2)

IF @adminUserId = @targetUserId
    RAISERROR('You cannot remove yourself', 16, 3)

    -- statements below still execute and commit even though there was an error raised above
DELETE FROM ExploringGroups.MemberRole WHERE GroupId = @groupId AND UserId = @targetUserId
DELETE FROM ExploringGroups.Membership WHERE GroupId = @groupId AND UserId = @targetUserId

COMMIT

RETURN 0

призвание

exec exploringgroups.removemember '356048C5-BAB3-45C9-BE3C-A7227225DFDD', 'Crypton', 'Crypton'

Производит

Сообщение 50000, уровень 16, состояние 2, процедура RemoveMember, строка 20
Указанный пользователь не является администратором этой группы
Сообщение 50000, уровень 16, состояние 3, процедура RemoveMember, строка 24
Вы не можете удалить себя

я думал XACT_ABORT должен был откатить всю транзакцию, если она установлена ON?

2 ответа

Решение

На самом деле, он ведет себя именно так, как и предполагалось. XACT_ABORT действительно вызвало откат транзакции, поэтому при каких-либо изменениях данных до момента ошибки они будут откатываться. Однако это не повлияло на ход выполнения и не остановило выполнение хранимой процедуры, поэтому следующие два DELETE были выполнены как неявные транзакции. Явные RAISERROR не отменяют пакет.

Смотрите эту упрощенную версию:

create table #t(i int);
insert #t values(1);
go

alter procedure sp
as
set xact_abort on
begin tran
raiserror ('x', 16, 1);
print 'deleting';
delete #t;
commit;
go

exec sp
go

select * from #t
go

Единственной забавной вещью было то, что ошибка, связанная с отсутствием соответствующего BEGIN TRAN в команде COMMIT

С SEH, это перейдет в блок CATCH.

Вы должны использовать оператор THROW вместо подхода RAISERROR.

Используйте BEGIN TRY и BEGIN CATCH и фиксируйте транзакцию в обычном режиме или выполните откат в блоке CATCH.

BEGIN TRY - Выполнить вставку или выдать ошибку - Зафиксировать транзакцию END TRY BEGIN CATCH - Откат END CATCH;

Другие вопросы по тегам