当 INSERT INTO 失败时,为什么我的事务会自动回滚?

3

令人惊讶的是,我找不到任何与我遇到的问题相关的解释或文献。

针对这些 SQL 语句的情况:

SELECT 1 AS Test INTO #tmpTest    
BEGIN TRAN    
SELECT 1 AS Test INTO #tmpTest    
ROLLBACK TRAN

逐行执行时,第3行的SELECT ... INTO失败,如预期,出现以下错误信息:

数据库中已经存在名为“#tmpTest”的对象。

但是,在此之后,第4行的ROLLBACK语句失败,出现以下错误:

ROLLBACK TRANSACTION 请求没有相应的 BEGIN TRANSACTION。

即使事务在第2行已经成功BEGIN

我看过SQL Server - transactions roll back on error?,但答案不适用于此处,因为默认的xact_abortoff此外,Quassnoi的答案Raj More的答案相矛盾。

到底是什么原因呢?


1
我已经尝试过并在SQL 2017上遇到了同样的问题。有趣。 - Vitaly Borisov
使用SQL Server 2016,在第二个SELECT语句前后添加了SELECT @@TRANCOUNT,然后逐行执行。第一个返回(如预期)1,然后SELECT失败,然后第二个@@TRANCOUNT返回0。_但是_,将错误的SELECT语句放在try / catch中,进入CATCH块仍然存在未提交的事务。耐人寻味。 - Eric Brandt
我已经获取了这条信息:“Msg 3998,Level 16,State 1,Line 1在批处理结束时检测到无法提交的事务。该事务已被回滚。” - Vitaly Borisov
另一个答案比你接受的那个更准确。批量中止是您所看到行为的关键。 - Martin Smith
猜想你是对的,@MartinSmith。 - Marc.2377
2个回答

3

2
根据微软文章XACT_STATE (Transact-SQL),当发生导致事务被分类为不可提交事务的错误时,请求无法提交事务或回滚到保存点;只能请求完全回滚事务。运行以下内容:
SELECT 1 AS Test INTO #tmpTest    
SELECT @@TRANCOUNT, XACT_STATE()
BEGIN TRAN    
SELECT @@TRANCOUNT, XACT_STATE()

然后:

BEGIN TRY
    SELECT 1 AS Test INTO #tmpTest    
END TRY
BEGIN CATCH
    SELECT @@ERROR, ERROR_MESSAGE()
    SELECT @@TRANCOUNT, XACT_STATE()
END CATCH

在块中的SELECT语句返回了如下信息: "在数据库中已经存在名为'#tmpTest'的对象。", @@TRANCOUNT值为1,但是XACT_STATE值为-1,因此SSMS中显示的错误信息为:

Msg 3998, Level 16, State 1, Line 1 检测到无法提交的事务。该事务已回滚。

接下来的SELECT @@TRANCOUNT返回值为0


"Original Answer"翻译成"最初的回答"

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