SQL 没有回滚整个 SqlCommand 的错误
SQL error not rolling back entire SqlCommand
我一直在将 set xact_abort on
放入 SQL 命令语句中,并注意到它不会在我的 C# SqlCommand
中回滚更新、插入等错误。摘自 this post. The MSDN 指出:
When SET XACT_ABORT is ON, if a Transact-SQL statement raises a run-time error, the entire transaction is terminated and rolled back.
我的 SQl 查询的一般格式是:
set xact_abort on
INSERT INTO Table1
UPDATE Table2
UPDATE Table3
UPDATE Table4
--Finally
SELECT someValue
我注意到我的 SQL 命令出错时没有被回滚。这种情况下的特定错误是一个参数的数据长度超过了指定的列长度。我正在使用 SqlCommand
和 SqlParameter
创建 SQL 查询。
我不打算处理 SQL 中的异常,但重要的是任何错误都不会对数据库进行任何更改。
典型的错误包括:列不存在、数据类型错误、数据会因长度而被截断等。
我应该使用 set xact_abort on
以外的东西吗?提前致谢!
您从 MSDN 引用的声明是正确的:将 XACT_ABORT
设置为 ON
,任何 错误将中止批处理并且 roll-back任何活跃的交易。这里的困惑在于,在SQLServer中,默认情况下每条语句本身就是一个Transaction。如果你想将多个语句组合成一个显式事务,那么你需要使用 BEGIN TRAN;
和 COMMIT;
。以下示例说明了此行为:
运行这个:
SET XACT_ABORT ON;
CREATE TABLE #Bob (ID INT);
INSERT INTO #Bob (ID) VALUES (1);
BEGIN TRAN;
INSERT INTO #Bob (ID) VALUES (2);
INSERT INTO #Bob (ID) VALUES (3);
INSERT INTO #Bob (ID) VALUES (4 / 0);
COMMIT TRAN;
然后 运行 这分开(因为上面的语句中的错误将中止整个批处理 - 由于使用 XACT_ABORT ON
- 所以 SELECT
将永远不会执行如果您尝试同时 运行 SELECT
):
SELECT * FROM #Bob;
它将 return 包含 1
的单行,因为该语句由其自身执行,而不是在显式事务中执行。一旦将 BEGIN TRAN;
和 COMMIT TRAN;
语句添加到您的代码中,它就会按您预期的那样工作。
从 C# 代码处理事务的一种模式如下:
try
{
var connection = new SqlConnection(connectionString);
connection.Open();
var trans = connection.BeginTransaction();
using (var command = connection .CreateCommand())
{
command.Transaction = trans;
command.CommandText = "...";
command.ExecuteNonQuery();
}
// other commands may be defined here
// command can be included or excluded from transaction (do not set Transaction property)
// commits the transaction
trans.Commit();
}
// best practice is to catch specific exception types like `SqlException`
catch (Exception ex) //error occurred
{
trans.Rollback();
// log error somewhere
}
finally
{
// execute no-matter what
}
这种模式有以下优点:
- 无需担心
SET XACT_ABORT
(默认关闭)
- 更好的异常处理
注:您可能对Unit of Work pattern
感兴趣
我一直在将 set xact_abort on
放入 SQL 命令语句中,并注意到它不会在我的 C# SqlCommand
中回滚更新、插入等错误。摘自 this post. The MSDN 指出:
When SET XACT_ABORT is ON, if a Transact-SQL statement raises a run-time error, the entire transaction is terminated and rolled back.
我的 SQl 查询的一般格式是:
set xact_abort on
INSERT INTO Table1
UPDATE Table2
UPDATE Table3
UPDATE Table4
--Finally
SELECT someValue
我注意到我的 SQL 命令出错时没有被回滚。这种情况下的特定错误是一个参数的数据长度超过了指定的列长度。我正在使用 SqlCommand
和 SqlParameter
创建 SQL 查询。
我不打算处理 SQL 中的异常,但重要的是任何错误都不会对数据库进行任何更改。
典型的错误包括:列不存在、数据类型错误、数据会因长度而被截断等。
我应该使用 set xact_abort on
以外的东西吗?提前致谢!
您从 MSDN 引用的声明是正确的:将 XACT_ABORT
设置为 ON
,任何 错误将中止批处理并且 roll-back任何活跃的交易。这里的困惑在于,在SQLServer中,默认情况下每条语句本身就是一个Transaction。如果你想将多个语句组合成一个显式事务,那么你需要使用 BEGIN TRAN;
和 COMMIT;
。以下示例说明了此行为:
运行这个:
SET XACT_ABORT ON;
CREATE TABLE #Bob (ID INT);
INSERT INTO #Bob (ID) VALUES (1);
BEGIN TRAN;
INSERT INTO #Bob (ID) VALUES (2);
INSERT INTO #Bob (ID) VALUES (3);
INSERT INTO #Bob (ID) VALUES (4 / 0);
COMMIT TRAN;
然后 运行 这分开(因为上面的语句中的错误将中止整个批处理 - 由于使用 XACT_ABORT ON
- 所以 SELECT
将永远不会执行如果您尝试同时 运行 SELECT
):
SELECT * FROM #Bob;
它将 return 包含 1
的单行,因为该语句由其自身执行,而不是在显式事务中执行。一旦将 BEGIN TRAN;
和 COMMIT TRAN;
语句添加到您的代码中,它就会按您预期的那样工作。
从 C# 代码处理事务的一种模式如下:
try
{
var connection = new SqlConnection(connectionString);
connection.Open();
var trans = connection.BeginTransaction();
using (var command = connection .CreateCommand())
{
command.Transaction = trans;
command.CommandText = "...";
command.ExecuteNonQuery();
}
// other commands may be defined here
// command can be included or excluded from transaction (do not set Transaction property)
// commits the transaction
trans.Commit();
}
// best practice is to catch specific exception types like `SqlException`
catch (Exception ex) //error occurred
{
trans.Rollback();
// log error somewhere
}
finally
{
// execute no-matter what
}
这种模式有以下优点:
- 无需担心
SET XACT_ABORT
(默认关闭) - 更好的异常处理
注:您可能对Unit of Work pattern
感兴趣