首页 > 解决方案 > 为什么在使用 XACT_ABORT 时该事务不回滚?

问题描述

我正在尝试学习有关隐式和显式事务的知识。我有一个包含三列的表和年龄检查约束(必须<10)

现在我有以下代码,因为我有XACT_ABORT ON. 但是第一条语句被导入到表中。怎么来的?

SET IMPLICIT_TRANSACTIONS OFF;
SET XACT_ABORT ON;

INSERT INTO tblbegintran (firstname, lastname, age)
VALUES ('gijs', 'adsafads', 6)
 
INSERT INTO tblbegintran (firstname, lastname, age)
VALUES ('gijss', 'dafds', 17)
COMMIT TRAN;

标签: sqlsql-servertsql

解决方案


首先,让我们从SET IMPLICIT_TRANSACTIONS. 从SET IMPLICIT_TRANSACTIONS (Transact-SQL)

关闭时,前面的每个 T-SQL 语句都由一个看不见的 BEGIN TRANSACTION 和一个看不见的 COMMIT TRANSACTION 语句限定。当关闭时,我们说事务模式是自动提交。如果您的 T-SQL 代码明显发出 BEGIN TRANSACTION,我们说事务模式是显式的。

由于您没有明确BEGIN TRANSACTION的上述事务,因此您处于自动提交状态;我已将相关句子加粗。从自动提交模式

自动提交模式下,每个数据库操作都是在执行时提交的事务。此模式适用于由单个 SQL 语句组成的许多实际事务。没有必要划定或指定这些事务的完成。在不支持事务的数据库中,自动提交模式是唯一支持的模式。在这样的数据库中,语句在执行时被提交,并且无法回滚;因此,它们始终处于自动提交模式。

如果底层 DBMS 不支持自动提交模式事务,驱动程序可以通过在执行每个 SQL 语句时手动提交来模拟它们。

如果以自动提交模式执行一批 SQL 语句,则提交该批中的语句时是特定于数据源的。它们可以在执行时提交,也可以在整个批处理执行后作为整体提交。一些数据源可能支持这两种行为,并可能提供一种选择其中一个或其他的方式。特别是,如果在批处理过程中发生错误,则已执行的语句是提交还是回滚取决于数据源。因此,使用批处理并要求将它们作为一个整体提交或回滚的可互操作应用程序应仅在手动提交模式下执行批处理。

最后,从SET XACT_ABORT (Transact-SQL)

当 SET XACT_ABORT 为 ON 时,如果 Transact-SQL 语句引发运行时错误,则整个事务将终止并回滚。

因此,在这里,您的每个语句在完成都会被隐式提交,因为您处于自动提交模式,因此每个语句都在它自己的事务中。因此,您的第一个满足要求的语句CHECK CONSTRAINT被提交,然后第二个语句失败,并且事务被回滚。这意味着XACT_ABORT仅回滚您尝试失败的事务;其中age的值为17

要获得您想要的行为,您需要使用SET IMPLICIT_TRANSACTIONS ON或将整个事物包装在显式事务中。例子:

CREATE TABLE dbo.tblbegintran (age int CHECK (age < 10));
GO
SET IMPLICIT_TRANSACTIONS OFF;
SET XACT_ABORT ON;
INSERT INTO tblbegintran (age)
VALUES (6);
INSERT INTO tblbegintran (age)
VALUES (17);
COMMIT TRAN;
GO

SET IMPLICIT_TRANSACTIONS OFF;
BEGIN TRANSACTION;
INSERT INTO tblbegintran (age)
VALUES (7);
INSERT INTO tblbegintran (age)
VALUES (17);
COMMIT TRANSACTION;
GO
SET IMPLICIT_TRANSACTIONS ON;
INSERT INTO tblbegintran (age)
VALUES (8);
INSERT INTO tblbegintran (age)
VALUES (17);
COMMIT TRANSACTION;
GO
SELECT * --Only 6 appears
FROM dbo.tblbegintran;
GO
DROP TABLE dbo.tblbegintran;

db<>小提琴


推荐阅读