SET XACT_ABORT ON Игнорируется, транзакция продолжается (SQL Server 2008 R2)
Возможно, я что-то упускаю, но хотя RAISERROR
s ниже имеют серьезность 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;