general 为 raiserror 时如何提交嵌套存储过程

How to commit nested stored procedure, when general is raiserror

我有一个存储过程 proc_in 将数据插入 tbl table

create table tbl(id int identity, val nvarchar(50))

create procedure proc_in
as
begin
    insert into tbl(val)
    values ('test')
end

我有 proc_out,我打电话给 proc_in

create procedure proc_out
as
begin
    exec proc_in

    DECLARE @MessageText NVARCHAR(100);
    SET @MessageText = N'This is a raiserror %s';
    RAISERROR(@MessageText, 16, 1, N'MSG')
end

我怎么写 proc_out 它 return raiserror 总是插入 TBL table。 我这样调用 proc_out

begin tran 
    declare @err int = 0
    exec @err = proc_out
if @ERR = 0 
    commit tran 
else 
    rollback tran

您将调用包装在调用上下文中的单个事务中,因此:

begin tran 
    declare @err int = 0
    exec @err = proc_out
if @ERR = 0 
    commit tran 
else 
    rollback tran

将始终回滚该事务中发生的所有内容。

避免这种情况的一种方法是将交易移动到您的 'proc_out' SP 中,例如

create procedure proc_out
as
begin
    set nocount, xact_abort on;

    exec proc_in;

    begin tran;

    -- All your other code

    if @Err = 1 begin
        rollback;

        declare @MessageText nvarchar(100);
        set @MessageText = N'This is a raiserror %s';
        --raiserror(@MessageText, 16, 1, N'MSG');
        -- Actually for most cases now its recommended to use throw
        throw 51000, @MessageText 1; 
    end; else begin
        commit;
    end;

    return 0;
end;

或者,我还没有尝试过,您可以尝试使用 savepoint,例如

create procedure proc_out
as
begin
    set nocount on;

    exec proc_in;

    save transaction SavePoint1;

    declare @MessageText nvarchar(100);
    set @MessageText = N'This is a raiserror %s';
    raiserror(@MessageText, 16, 1, N'MSG');

    return 0;
end;

然后将其命名为:

begin tran;

declare @err int = 0;
exec @err = proc_out;

if @ERR = 0;
    commit tran;
end; else begin
    rollback tran SavePoint1;
    commit tran;
end;

虽然我不喜欢这种方法,因为您的 SP 内部工作的知识现在已经泄漏到调用上下文中。

并且一些错误无论如何都会回滚整个事务。

了解此处的 XACT_ABORT 设置很重要。

When SET XACT_ABORT is OFF, in some cases only the Transact-SQL statement that raised the error is rolled back and the transaction continues processing. Depending upon the severity of the error, the entire transaction may be rolled back even when SET XACT_ABORT is OFF. OFF is the default setting in a T-SQL statement, while ON is the default setting in a trigger.