0

也许我遗漏了一些东西,但即使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'

生产

Msg 50000,Level 16,State 2,Procedure RemoveMember,Line 20
指定的用户不是该组的管理员
Msg 50000,Level 16,State 3,Procedure RemoveMember,Line 24
你不能删除自己

我认为XACT_ABORT如果设置为应该回滚整个事务ON

4

2 回答 2

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

唯一有趣的是,关于 COMMIT 没有相应的 BEGIN TRAN 的错误被吞下了。

使用 SEH,它会跳转到 CATCH 块。

于 2014-05-01T18:20:27.617 回答
1

您应该使用“THROW”语句而不是 RAISERROR 方法。

使用 BEGIN TRY 和 BEGIN CATCH 并在 CATCH 块中正常提交事务或回滚。

BEGIN TRY -- 插入或抛出错误 -- 提交事务 END TRY BEGIN CATCH -- 回滚 END CATCH;

于 2014-05-01T17:58:38.580 回答