SQL Server中TRANSACTION的范围是什么?

8

我正在创建一个存储过程,发现我的方法与同事的方法有些不同。

我正在使用 SQL Server 2005

  1. My Stored procedure looks like this

    BEGIN TRAN
        BEGIN TRY
            INSERT INTO Tags.tblTopic 
                (Topic, TopicCode, Description)
                VALUES(@Topic, @TopicCode, @Description)
            INSERT INTO Tags.tblSubjectTopic
                (SubjectId, TopicId)
                VALUES(@SubjectId, @@IDENTITY)
          COMMIT TRAN
    END TRY
    
    BEGIN CATCH
            DECLARE @Error VARCHAR(1000)
            SET @Error= 'ERROR NO : '+ERROR_NUMBER() + ', LINE NO : '+ ERROR_LINE() + ', ERROR MESSAGE : '+ERROR_MESSAGE()
            PRINT @Error
            ROLLBACK TRAN
    END CATCH
    
  2. And my colleague's way of writing looks like the below one

    BEGIN TRY
        BEGIN TRAN
            INSERT INTO Tags.tblTopic 
                (Topic, TopicCode, Description)
                VALUES(@Topic, @TopicCode, @Description)
            INSERT INTO Tags.tblSubjectTopic
                (SubjectId, TopicId)
                VALUES(@SubjectId, @@IDENTITY)
       COMMIT TRAN
    END TRY
    
    BEGIN CATCH
            DECLARE @Error VARCHAR(1000)
            SET @Error= 'ERROR NO : '+ERROR_NUMBER() + ', LINE NO : '+ ERROR_LINE() + ', ERROR MESSAGE : '+ERROR_MESSAGE()
            PRINT @Error
            ROLLBACK TRAN
    END CATCH
    
这里两种方法唯一的区别就是Begin TRAN的位置。根据我的理解,我同事的方法在出现异常时不应该起作用,即因为方法2中TRAN没有作用域,所以Rollback不应该被执行。但是当我尝试运行这两种方法时,它们以相同的方式工作。
在方法1中,TRAN的作用域在try块之外,因此应在try块和catch块中可见,并且应根据编程的作用域方法给出结果。
在方法2中,TRAN的作用域在Try块中受限,因此CommitRollback应在try块内发生,并且应在没有Begin Tran存在于catch块中的Rollback时引发异常,但这也完美地工作。
我对TRANSACTION如何工作感到困惑。它是否无作用域?

3
顺便提一句,永远不要使用@@identity。如果有人将触发器添加到插入另一个带有标识列的表中,则它会给您错误的值。即使现在没有触发器,这也是非常糟糕的做法。这会破坏数据完整性,并且当发生此问题时很难修复。改用scope_Identity()或2008年版本中的output子句。此外,最佳实践是立即将其放入变量中,而不是在下一条语句中使用它。当有人在这两个语句之间添加语句时,维护会发生什么? - HLGEM
@HLGEM 谢谢,我6个月前遇到了这个问题。我知道触发器在这种情况下会出问题,但当时我没有其他选择,因为我不知道其他方法。再次感谢您的回答,并点赞。 - Shantanu Gupta
2个回答

10

事务的“作用域”不像程序语言那样。

对于当前连接,事务是嵌套的。每个BEGIN TRAN开始一个新的事务,只要调用COMMIT或ROLLBACK,这个事务就会结束,无论它在存储过程的哪个位置。


2

当前连接中的事务是嵌套的。每个BEGIN TRAN开始一个新的事务,而该事务在调用COMMIT或ROLLBACK时结束,无论在存储过程中的哪个位置。

只需补充一点,ROLLBACK会结束连接中的“所有”打开的事务...


@ebis:您能在这里更详细地解释一下吗?我没有完全明白您在最后一行要表达的意思。 - Shantanu Gupta
BEGIN TRAN; .... Statements ... BEGIN TRY; BEGIN TRAN; ... STATEMENTS WITH Error ... COMMIT; -- 这个COMMIT将不会被执行 END TRY BEGIN CATCH; ROLLBACK; --两个打开的事务都将被回滚 END CATCH; COMMIT; -- Tranlevel = 0,这个COMMIT将失败 -- 如果您有一堆存储过程调用的堆栈,其中每个(或某些)打开一个事务,并且由于某些错误而在中间进行回滚,则NOT好 -- 可能更清楚我是什么意思(可能不是) - ebis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接