Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sql-server-2005/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql server 2005 包含TRY-CATCH回滚模式的嵌套存储过程?_Sql Server 2005_Linq To Sql_Stored Procedures_Transactions - Fatal编程技术网

Sql server 2005 包含TRY-CATCH回滚模式的嵌套存储过程?

Sql server 2005 包含TRY-CATCH回滚模式的嵌套存储过程?,sql-server-2005,linq-to-sql,stored-procedures,transactions,Sql Server 2005,Linq To Sql,Stored Procedures,Transactions,我对以下模式的副作用和潜在问题感兴趣: CREATE PROCEDURE [Name] AS BEGIN BEGIN TRANSACTION BEGIN TRY [...Perform work, call nested procedures...] END TRY BEGIN CATCH ROLLBACK TRANSACTION RAISERROR [rethrow caught error using @Erro

我对以下模式的副作用和潜在问题感兴趣:

CREATE PROCEDURE [Name]
AS
BEGIN
    BEGIN TRANSACTION
    BEGIN TRY
        [...Perform work, call nested procedures...]
    END TRY
    BEGIN CATCH
        ROLLBACK TRANSACTION
        RAISERROR [rethrow caught error using @ErrorNumber, @ErrorMessage, etc]
    END CATCH
END
据我所知,当与单个过程一起使用时,此模式是合理的-该过程要么会无错误地完成其所有语句,要么会回滚所有操作并报告错误

但是,当一个存储过程调用另一个存储过程来执行某些子工作单元时(理解为较小的过程有时会自己调用),我看到一个与回滚相关的问题—一条信息性消息(级别16)发出声明回滚事务请求没有相应的开始事务。。我假设这是因为子过程中的回滚总是回滚最外层的事务,而不仅仅是子过程中启动的事务

如果发生任何错误(并且错误作为SQL错误报告给客户机),我确实希望整个过程回滚并中止,但我不确定是否有来自外层的所有副作用试图回滚已经回滚的事务。在每个TRY-CATCH层执行回滚之前,是否检查
@@tracount

最后是客户端(Linq2SQL),它有自己的事务层:

try
{
    var context = new MyDataContext();
    using (var transaction = new TransactionScope())
    {       
            // Some Linq stuff
        context.SubmitChanges();
        context.MyStoredProcedure();
        transactionComplete();
    }
}
catch
{
    // An error occured!
}
如果在MyStoredProcedure中调用的存储过程“MySubProcedure”引发错误,我是否可以确保以前在MyStoredProcedure中完成的所有操作都将回滚,所有由SubmitChanges执行的Linq操作都将回滚,并最终记录错误?或者我需要在我的模式中改变什么以确保整个操作是原子的,同时仍然允许单独使用子部分(即子过程应该仍然具有相同的原子保护)

我不是Linq guy(Erland也不是),但他写了关于错误处理的绝对圣经。除了Linq可能增加您的问题的复杂性之外,您的所有其他问题都应该在这里回答:

(旧链接:)

这是我们的模板(错误日志记录已删除)

这是为了处理

  • 保罗·兰德尔的文章
  • 错误266
  • 触发器回滚
解释:

  • 所有TXN begin和commit/ROLLBACK必须成对,以便
    @@TRANCOUNT
    在进入和退出时相同

  • @@TRANCOUNT
    的不匹配会导致错误266,因为

    • BEGIN TRAN
      递增
      @@tracount

    • COMMIT
      递减
      @@tracount

    • 回滚
      @@tracount
      返回为零

  • 对于当前作用域,不能递减
    @@tracount

    这就是你认为的“内部交易”

  • 将XACT\U ABORT设置为ON
    抑制由不匹配的
    @@tracount引起的错误266

    还可以在dba.se上处理类似的问题

  • 这允许客户端TXN(如LINQ) 单个存储过程可以是分布式或XA事务的一部分,也可以只是在客户端代码中启动的一个存储过程(例如.net TransactionScope)

用法:

  • 每个存储的进程必须符合相同的模板
摘要

  • 因此,不要创建超过您需要的TXN
代码

CREATE PROCEDURE [Name]
AS
SET XACT_ABORT, NOCOUNT ON

DECLARE @starttrancount int

BEGIN TRY
    SELECT @starttrancount = @@TRANCOUNT

    IF @starttrancount = 0
        BEGIN TRANSACTION

       [...Perform work, call nested procedures...]

    IF @starttrancount = 0 
        COMMIT TRANSACTION
END TRY
BEGIN CATCH
    IF XACT_STATE() <> 0 AND @starttrancount = 0 
        ROLLBACK TRANSACTION;
    THROW;
    --before SQL Server 2012 use 
    --RAISERROR [rethrow caught error using @ErrorNumber, @ErrorMessage, etc]
END CATCH
GO
创建过程[名称]
作为
设置XACT_中止,不计数为ON
声明@starttrancount int
开始尝试
选择@starttrancount=@@TRANCOUNT
如果@starttrancount=0
开始交易
[…执行工作,调用嵌套过程…]
如果@starttrancount=0
提交事务
结束尝试
开始捕捉
如果XACT_STATE()为0且@starttrancount=0
回滚事务;
投掷;
--在SQL Server 2012之前使用
--RAISERROR[使用@ErrorNumber、@ErrorMessage等重新显示捕获的错误]
端接
去
注意事项:

  • 回滚检查实际上是冗余的,因为
    将XACT\u ABORT设置为ON
    。然而,它让我感觉更好,没有它看起来很奇怪,并且允许出现你不想穿的情况

  • 具有使用保存点的。我更喜欢原子数据库调用,不使用像他们文章那样的部分更新


要解决返回@AlexKuznetsov提到的错误号和行号的问题,可以提出如下错误:

DECLARE @ErrorMessage NVARCHAR(4000)
DECLARE @ErrorSeverity INT
DECLARE @ErrorState INT
DECLARE @ErrorLine INT
DECLARE @ErrorNumber INT

SELECT @ErrorMessage = ERROR_MESSAGE(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE(),
@ErrorNumber = ERROR_NUMBER(),
@ErrorLine = ERROR_LINE()

RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorNumber, @ErrorLine)
--上面的@Amanda方法没有返回正确的错误号

DECLARE  
  @ErrorMessage   nvarchar(4000),  
  @ErrorSeverity   int,  
  @ErrorState int,  
  @ErrorLine  int,  
  @ErrorNumber   int  

BEGIN TRY  
 SELECT 1/0; -- CATCH me  
END TRY  

BEGIN CATCH  

  DECLARE @err int = @@ERROR  

  PRINT @err           -- 8134, divide by zero  
  PRINT ERROR_NUMBER() -- 8134  

  SELECT  
    @ErrorMessage  = ERROR_MESSAGE(),  
    @ErrorSeverity = ERROR_SEVERITY(),  
    @ErrorState    = ERROR_STATE(),  
    @ErrorNumber   = ERROR_NUMBER(),  
    @ErrorLine     = ERROR_LINE()  

  -- error number = 50000 :(  
  RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorNumber, @ErrorLine)  

END CATCH  

-- error number = 8134  
SELECT 1/0

如果CATCH中不需要特殊的错误处理,除了rethrow和stored procs调用链不太长,则可能适合使用以下简单模板:

create procedure someNestedSP
as
SET XACT_ABORT ON
begin transaction
-- do some work or call some other similar SP
commit transaction
如果出现任何错误,它还将使用所有“嵌套”事务回滚根事务,但代码比@gbn的解决方案更短、更直接。仍然
XACT_ABORT
处理了这里提到的大多数问题


事务嵌套可能会有额外的开销,但我想可能不会太高。

如果您的子过程依赖于外部过程(即事务)中的某些内容,那么在我看来,在不应该存在耦合的地方似乎存在大量耦合。编辑:我想我读到了你的问题错误设置NOCOUNT ON似乎没有必要?你应该总是在imho上使用SET NOCOUNT。@Carl R:SET NOCOUNT ON在代码和触发器中非常重要:没有它,很多客户端代码都会中断。此外,还可以测量处理“xx行受影响消息”所需的时间。请看(我的问题)。我认为@ROWCOUNT没有随着它的启用而更新,但我错了。谢谢@Niraj:如果您决定将存储过程嵌套得更深,或者希望直接调用内部存储过程,该怎么办。你在做假设。如果内部@@TRANCOUNT变为零(例如,批处理中止触发器错误),您仍然可以得到错误266