GO命令在事务中的奇怪行为

Chr*_*ian 3 sql sql-server

如果我运行以下内容:

CREATE TABLE t1  
    (a INT NOT NULL PRIMARY KEY);  
CREATE TABLE t2  
    (a INT NOT NULL REFERENCES t1(a));  

INSERT INTO t1 VALUES (1);  
INSERT INTO t1 VALUES (3);  
INSERT INTO t1 VALUES (4);  
INSERT INTO t1 VALUES (6);  
GO  
Run Code Online (Sandbox Code Playgroud)

然后我运行这个:

SET XACT_ABORT ON; 

BEGIN TRANSACTION
INSERT INTO t2 VALUES (1)

INSERT INTO t2 VALUES (2) -- Foreign key error, entire transaction rolled back

INSERT INTO t2 VALUES (3)

COMMIT TRANSACTION
Run Code Online (Sandbox Code Playgroud)

一切都好; 事务被回滚,因为我们在t1中没有值2,因此表t2为空

但是,如果我GO在此事务中间添加一个命令,则事务不会回滚,并且数字3将插入到表t2中.这是代码:

SET XACT_ABORT ON; 

BEGIN TRANSACTION
INSERT INTO t2 VALUES (1)

INSERT INTO t2 VALUES (2) -- Foreign key error.  

GO  --this command breaks the transaction

INSERT INTO t2 VALUES (3)

COMMIT TRANSACTION
Run Code Online (Sandbox Code Playgroud)

我知道这GO不是一个sql语句,而是一个实际上没有发送到SQL服务器的SQL Server Management Studio实用程序命令.

那么,为什么在运行上面的最后一个代码片段(包含GO命令的代码片段)后,我可以看到表t2中的数字3

我使用过SQL Server Management Studio

Mar*_*ith 8

如果SET XACT_ABORTON,如果Transact-SQL语句引发运行时错误,则终止并回滚整个事务.

在第一批打开的事务会在出错时自动回滚.

然后SSMS看到有另一批要处理并开火

INSERT INTO t2 VALUES (3)

COMMIT TRANSACTION
Run Code Online (Sandbox Code Playgroud)

当第一个事务现在关闭时,这将启动一个新的自动提交事务.并且最后会出现意外情况,COMMIT TRANSACTION从而引发错误.

您可以更改第二批以检查第一批中的事务是否被毫不客气地回滚.

SET XACT_ABORT ON; 

BEGIN TRANSACTION
INSERT INTO t2 VALUES (1)

INSERT INTO t2 VALUES (2) -- Foreign key error.  

GO 

IF @@TRANCOUNT = 0
    BEGIN
    RAISERROR('Transaction closed',16,1);
    RETURN;
    END

INSERT INTO t2 VALUES (3)

COMMIT TRANSACTION
Run Code Online (Sandbox Code Playgroud)

或者您可以打开SQLCMD模式并使用

:on error exit
Run Code Online (Sandbox Code Playgroud)

如果您不想在错误后处理更多批次.

在此输入图像描述

  • 一如既往地发现. (4认同)
  • @Christian - 我复制并粘贴,肯定会看到3.再次检查.并确保在开始之前表格是空的. (3认同)
  • 这在我的回答中得到了解释.第一个tran回滚,因为您使用`XACT_ABORT`请求,然后开始新的. (2认同)
  • 如果FK约束引起错误,则没有设置或条件允许(2)放入该表.我能想到的唯一解释就是它之前存在. (2认同)
  • @Christian - SQL Server对您要提交的未来批次一无所知.SSMS只提交批次`BEGIN TRANSACTION INSERT INTO t2 VALUES(1); INSERT INTO t2 VALUES(2);`最初.遇到错误并且由于您的`XACT_ABORT`设置而回滚事务.然后SSMS在第一个交易已经死亡后提交第二批****您也可以改变它以检查`@@ trancount`是否为预期的1. (2认同)
  • @Christian - 在合法的情况下,您希望保持事务处于打开状态,使用adhoc查询手动检查结果,然后在准备好时提交. (2认同)
  • 在T-SQL @Christian大多数语句一样`BEGIN..END`和'BEGIN TRY..END TRY`等都是*结构*.也就是说,它们不是可执行的,而是形成代码结构,因此要编译"BEGIN"必须具有"END".但是`BEGIN TRANSACTION`和`COMMIT`(以及`ROLLBACK`)*不是*结构,而是它们实际上是*可执行*语句,它们会改变当前的会话状态.这就是为什么没有`END TRANSACTION`,以及为什么在编译`BEGIN TRANSACTION`时不必存在`COMMIT`,因此事务如何成为会话级状态/实体. (2认同)