Sql server 如何回滚存储过程中的事务?

Sql server 如何回滚存储过程中的事务?,sql-server,tsql,Sql Server,Tsql,当内部SP尝试回滚事务时,它已完成,但出现错误: Msg 266,16级,状态2,程序ptest,第0行[批次开始行] 37]执行后的事务计数表示不匹配的 开始并提交语句。上一次计数=1,当前计数=0 是否可以回滚内部SP内的事务 IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ptest]') AND type in (N'P', N'PC')) BEGIN EXEC dbo.sp_ex

当内部SP尝试回滚事务时,它已完成,但出现错误:

Msg 266,16级,状态2,程序ptest,第0行[批次开始行] 37]执行后的事务计数表示不匹配的 开始并提交语句。上一次计数=1,当前计数=0

是否可以回滚内部SP内的事务

IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ptest]') AND type in (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql @statement = N'CREATE PROCEDURE [dbo].[ptest] AS'
END
GRANT EXECUTE on [dbo].[ptest] to public;
GO
ALTER  PROCEDURE [dbo].[ptest]
@parrollback bit = 0
AS
BEGIN
SET NOCOUNT ON
SET XACT_ABORT OFF
select @@TRANCOUNT as '@@TRANCOUNT:[ptest] '
if @parrollback is not null and @parrollback>0
    if @@TRANCOUNT>0 rollback tran;
END
GO

IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[pcaller]') AND type in (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql @statement = N'CREATE PROCEDURE [dbo].[pcaller] AS'
END
GRANT EXECUTE on [dbo].[pcaller] to public;
GO
ALTER  PROCEDURE [dbo].[pcaller]
AS
BEGIN
SET NOCOUNT ON
begin tran
select @@TRANCOUNT as '@@TRANCOUNT: before [ptest]'
exec ptest 1
select @@TRANCOUNT as '@@TRANCOUNT: after [ptest] '
if @@TRANCOUNT>0 rollback tran;
END
GO
-------------

exec pcaller 

/*
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ptest]') AND type in (N'P', N'PC'))
    drop proc pcaller
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ptest]') AND type in (N'P', N'PC'))
    drop proc ptest
*/

尽量不要在子过程内处理父事务(当XACT_STATE()=-1时出现异常)。在启动事务的“执行”级别处理该事务

如果在父事务中执行过程,则创建保存点并在需要时回滚到该保存点。捕获子过程的执行结果,并在父级处理事务(如果父级是启动事务的父级)

创建或更改过程[dbo].[ptest]@parrollback位=0
作为
开始
不计较
将XACT_中止设置为OFF
声明@trancount INT=@@trancount;
如果@trancount=0
开始
开始交易;
结束
其他的
开始
保存事务MySavepoint;
结束
--做些事情。。。。。。。。。
--何时提交或检查错误
--假设@parrollback是主控制标准
如果@parrollback=1
开始
如果@trancount=0
开始
回滚事务;
返回(0);
结束
其他的
开始
回滚事务MySavePoint
申报表(1);
结束
结束
--为了测试的完整性,只需处理@parrollback 1
如果@trancount=0
开始
提交事务;
结束
返回(0);
结束
去
创建或更改过程dbo.pcaller
作为
开始
不计较
声明@ptestexec INT;
开始交易
选择@@TRANCOUNT作为“@@TRANCOUNT:在[ptest]之前”
EXEC@ptestexec=dbo.ptest@parrollback=1;
如果@ptestexec=1
开始
回滚事务
结束
其他的
开始
提交事务
结束
--在事务之外执行ptest
EXEC@ptestexec=dbo.ptest@parrollback=0;
选择@@TRANCOUNT作为trancount1;
EXEC@ptestexec=dbo.ptest@parrollback=1;
选择@@TRANCOUNT作为trancount2;
--在事务之外执行ptest
开始交易;
--在父事务中执行的ptest
EXEC@ptestexec=dbo.ptest@parrollback=0;
选择@@TRANCOUNT作为trancount3--ptest不影响父事务
提交事务——或回滚
结束
去
EXEC dbo.pcaller
去

在哪一行出现此错误?
dbo.sp_executesql@statement=N'CREATE PROCEDURE[dbo].[pcaller]AS'
该语句的其余部分在哪里?
BEGIN TRAN
没有
COMMIT
;只有
回滚
,如果
@@TRANCOUNT
的值大于
0
@Lamu:是,在调用者中开始TRAN,然后内部处理此事务回滚。最后trancount=0,但当我调用exec pcaller时,它会生成一个错误。
CREATE OR ALTER PROCEDURE [dbo].[ptest] @parrollback bit = 0
AS
BEGIN
    SET NOCOUNT ON
    SET XACT_ABORT OFF

    DECLARE @trancount INT = @@TRANCOUNT;

    IF @trancount = 0
    BEGIN
        BEGIN TRANSACTION;
    END
    ELSE
    BEGIN
        SAVE TRANSACTION MySavepoint;
    END

    --do stuff.........


    --when it is time to commit or check for errors
    --assume @parrollback is the main control criterium

    IF @parrollback = 1
    BEGIN
        IF @trancount = 0
        BEGIN
            ROLLBACK TRANSACTION;
            RETURN(0);
        END
        ELSE
        BEGIN
            ROLLBACK TRANSACTION MySavePoint
            RETURN (1); 
        END
    END

    --just handle @parrollback <> 1, for completeness of the test
    IF @trancount = 0
    BEGIN
        COMMIT TRANSACTION;
    END

    RETURN (0);
END
GO

CREATE OR ALTER  PROCEDURE dbo.pcaller
AS
BEGIN
    SET NOCOUNT ON
    DECLARE @ptestexec INT;
    BEGIN TRANSACTION

    select @@TRANCOUNT as '@@TRANCOUNT: before [ptest]'
    EXEC @ptestexec = dbo.ptest @parrollback = 1;

    IF @ptestexec = 1
    BEGIN
        ROLLBACK TRANSACTION
    END
    ELSE
    BEGIN
        COMMIT TRANSACTION
    END


    --execute ptest, outside of a transaction
    EXEC @ptestexec = dbo.ptest @parrollback = 0;
    SELECT @@TRANCOUNT AS trancount1;

    EXEC @ptestexec = dbo.ptest @parrollback = 1;
    SELECT @@TRANCOUNT AS trancount2;

    --execute ptest, outside of a transaction
    BEGIN TRANSACTION;

    --ptest executed in a parent transaction
    EXEC @ptestexec = dbo.ptest @parrollback = 0;
    SELECT @@TRANCOUNT AS trancount3; --ptest does not affect the parent transactions

    COMMIT TRANSACTION --or rollback
END
GO

EXEC dbo.pcaller
GO