【问题标题】:T-SQL: Commit operations of nested stored procedure from outer stored procedureT-SQL:从外部存储过程提交嵌套存储过程的操作
【发布时间】:2020-02-24 13:36:27
【问题描述】:

我正在使用嵌套存储过程。开始事务和提交/回滚语句位于外部 SP 中。我可以将在嵌套 SP 中发生的所有数据库操作都提交到外部 SP 中吗?目前看来,它不像这样工作。是否有任何交易配置允许这样做?

ALTER procedure [dbo].[OuterStoredProcedure]
as
   begin
       declare @nRC int
       SET NOCOUNT ON

       begin transaction

       execute @nRC=InnerStoredProcedure /*includes update statements*/

        if (@nRC <> 1)
            rollback transaction
        else
            commit transaction
   end

【问题讨论】:

  • 向我们展示您的代码。如果您在事务中执行 SP,并且回滚,那么是的,这些 SP 的“操作”将被回滚。如果它没有按您的预期工作,那么您的交易声明似乎是错误的。
  • @Larnu,请看上面的代码
  • 那么这里有什么问题呢?关于立场:“如果您在事务中执行 SP,并且回滚,那么是的,这些 SP 的“操作”将被回滚。” 也许问题在于您有@nRC &lt;&gt; 1? SP 返回 0 表示成功,而不是 1
  • @Larnu,假设它返回 1 表示成功。主要问题是为什么不能将外部过程中的提交应用于内部过程中的更新语句?我想当我们调用另一个过程时,会启动一些内部事务,我们不能在多个嵌套存储过程中使用相同的事务。

标签: tsql stored-procedures transactions commit


【解决方案1】:

我为你准备了以下测试。如您所见,如果嵌套过程返回 0(作为错误),我们可以在第一个过程(父过程)中回滚

CREATE TABLE test1010
(
    ID Int identity (1,1),
    Name nvarchar(20)
)
GO

--DROP PROCEDURE dbo.A1
CREATE PROCEDURE dbo.A1
    @name nvarchar(20)
AS
    BEGIN
        INSERT INTO test1010 VALUES (@name)
        return 0
    END
GO

--DROP PROCEDURE dbo.AA
CREATE PROCEDURE dbo.AA
    @name1 nvarchar(20)
AS
    BEGIN
        DECLARE @nRC INT;
        SET NOCOUNT ON;

        BEGIN TRANSACTION;
        EXECUTE @nRC = dbo.A1 @name = @name1;
        IF(@nRC <> 1)
            ROLLBACK TRANSACTION;
        ELSE
            COMMIT TRANSACTION;
    END;
GO


SELECT * FROM test1010
GO
EXECUTE dbo.AA  @name1 = 'aa'
GO
SELECT * FROM test1010

还有其他事情。在每个过程中,我们必须检查交易数量。如果我们没有交易我们打开它,如果我们有我们保存它。最后我们检查,如果我们打开了事务,我们提交它,如果没有,我们让父过程处理事务。

你可以看到我的回答here

CREATE PROCEDURE Ardi_Sample_Test  
    @InputCandidateID INT  
AS  
    DECLARE @TranCounter INT;  
    SET @TranCounter = @@TRANCOUNT;  
    IF @TranCounter > 0  
        SAVE TRANSACTION ProcedureSave;  
    ELSE  
        BEGIN TRANSACTION;  
    BEGIN TRY  

        /*
        <Your Code>
        */

        IF @TranCounter = 0  
            COMMIT TRANSACTION;  
    END TRY  
    BEGIN CATCH  
        IF @TranCounter = 0  
            ROLLBACK TRANSACTION;  
        ELSE  
            IF XACT_STATE() <> -1  
                ROLLBACK TRANSACTION ProcedureSave;  

        DECLARE @ErrorMessage NVARCHAR(4000);  
        DECLARE @ErrorSeverity INT;  
        DECLARE @ErrorState INT;  
        SELECT @ErrorMessage = ERROR_MESSAGE();  
        SELECT @ErrorSeverity = ERROR_SEVERITY();  
        SELECT @ErrorState = ERROR_STATE();  

        RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState);  
    END CATCH  
GO  

在您的程序中始终使用这种模式。

【讨论】:

  • CATCH 块中只使用THROW 而不是所有这些代码来重新引发错误不是更好吗?
  • @GertArnold Microsoft 建议我们开始使用 THROW 语句而不是 RAISERROR。 THROW 语句似乎比 RAISERROR 简单易用。你有权利
猜你喜欢
  • 1970-01-01
  • 2018-10-03
  • 2013-05-28
  • 2011-08-03
  • 2020-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多