如果你小心并且在所有事情上使用TRY-CATCH,并且你真的需要使用错误回滚:
SET XACT_ABORT ON
换句话说,是否有任何TRY-CATCH会错过SET XACT_ABORT ON会处理的错误?
答案 0 :(得分:35)
请记住,TRY-CATCH无法使用或不使用XACT_ABORT
进行捕获。
但是,SET XACT_ABORT ON
不会影响错误的捕获。它确实保证任何事务都回滚/注定失败。当“关闭”时,您仍然可以选择提交或回滚(受xact_state约束)。这是SQL 2005对XACT_ABORT
如果客户端命令超时启动并且客户端发送“abort”指令,它还会删除锁等。如果没有SET XACT_ABORT
,则在连接保持打开状态时可以保留锁定。我的同事(MVP)和我在今年年初对此进行了彻底的测试。
答案 1 :(得分:3)
我认为执行分布式事务时需要SET XACT_ABORT ON。
From the books on line: 对于大多数OLE DB提供程序(包括SQL Server)的隐式或显式事务中的数据修改语句,必须将XACT_ABORT设置为ON。不需要此选项的唯一情况是提供程序是否支持嵌套事务。有关更多信息,请参阅分布式查询和分布式事务。
答案 2 :(得分:1)
XACT_ABORT确实会影响错误处理:它会在遇到错误时中止整个批处理,并且产生错误的行后面的任何代码(包括错误检查!)将永远不会执行。此行为有两个例外:XACT_ABORT被TRY ... CATCH取代(CATCH块将始终执行,事务不会自动回滚,只能呈现不可承受),XACT_ABORT将忽略RAISERROR。
答案 3 :(得分:1)
我的理解是,即使使用try catch并且在catch块中没有使用rollback语句,当XACT_ABORT
为ON时,任何不可提交的事务都将被回滚。
答案 4 :(得分:1)
盲目地总是使用SET XACT_ABORT ON
;这最近烧了我。
我在StackOverflow上阅读了一个令人信服的论点,建议你 总是使用XACT_ABORT ON
。我更改了系统以在连接期间设置该选项。除了它导致数据损坏和许多痛苦。
begin transaction
try
perform insert
catch duplicate key violation and react appropriately
perform more actions
commit transaction
catch
rollback transaction
end
除了您的“更多操作”将不再在交易中发生。因为即使您发现重复密钥违规,服务器也不再处于事务中:
begin transaction
try
perform insert
catch duplicate key violation and react appropriately
transaction implicitly rolled back
perform more actions
commit transaction -> fails because not in a transaction
catch
rollback transaction -> fails because not i a transaction
end
我已经扭转了自己。 从不使用SET XACT_ABORT ON
。
修改:人们似乎认为问题来自于尝试在不在交易中时调用ROLLBACK TRANSACTION
。如果交易没有进行,他们认为可以通过不调用ROLLBACK
来解决问题。
让我们使用一些伪代码,更改名称以保护NDA:
const
SQLNativeErrorPrimaryKeyViolation = 2627; //Primary keys. 2601 is for other unique index
void x(String sql)
{
database.Connection.ExecuteNoRecords(sql);
}
这是使这个答案更具可读性的迂腐方式;我们使用x
来表示某些SQL语句的执行:
void DoStuff()
{
x("BEGIN TRANSACTION");
try
{
try
{
x("INSERT INTO Patrons (AccountNumber, Name, Gender)"+
"VALUES (619, 'Shelby Jackson', 'W'");
}
catch (ESqlServerException e)
{
//check if the patron already exists (or some other hypothetical situation arises)
if (e.NativeError == SQLNativeErrorPrimaryKeyViolation)
{
//This patron already exists. Set their frob to grob because contoso the blingblong
x("UPDATE Patrons SET Frob='Grob' WHERE AccountNumber = 619");
//20110918: Dont forget we also need to bang the gardinker
x("EXECUTE BangTheGardinker @floof=619");
}
else
throw e;
}
//Continue with the stuff
x("EXECUTE Frob('{498BBB4D-D9F7-4438-B7A6-4AB5D57937C0}')");
//All done, commit the transaction
x("COMMIT TRANSACTION");
}
catch (Exception e)
{
//Something bad happened, rollback the transaction
//(if SQL Server didn't kill the transaction without our permission)
x("IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION");
throw e;
}
}
所以,该代码有效。如果我们期望出现错误,我们会处理并继续。这称为处理错误。如果发生了一些未知异常(我们没想到的话),我们rollback
可能正在进行的任何交易。
现在让我们看看我们是否盲目遵循XACT_ABORT
始终应该遵循的建议:
DbConnection Connection()
{
if (_connection == null)
{
_connection = new SqlConnection();
//It is generally recommended that you always have xact_abort on.
//If a connection is closed with a transaction still in progress
//it still leaves locks held until that connection is finally recycled
//Also, when querying linked severs in a client-side transaction, the
//operation won't work until xact_abort is on (SQL Server will throw an saying xactabort is off
_connection.ExecuteNoRecords("SET XACT_ABORT ON");
}
return _connection;
}
void x(String sql)
{
database.Connection.ExecuteNoRecords(sql);
}
DoStuff 以处理错误情况。但是,XACT_ABORT ON
引入连接现在会导致数据库损坏。对于那些没有看到错误的人,让我们来看看代码:
void DoStuff()
{
x("BEGIN TRANSACTION");
try
{
try
{
x("INSERT INTO Patrons (AccountNumber, Name, Gender)"+
"VALUES (619, 'Shelby Jackson', 'W'");
}
catch (ESqlServerException e)
{
//WARNING: WE ARE NO LONGER OPERATING IN A TRANASCTION
//Because XACT_ABORT is on, the transaction that we started has been implicitly rolled back.
//From now on, we are no longer in a transaction. If another error happens
//the changes we make cannot be rolled back
//check if the patron already exists (or some other hypothetical situation arises)
if (e.NativeError == SQLNativeErrorPrimaryKeyViolation)
{
//WARNING: This update happens outside of any transaction!
//This patron already exist. Set their frob to grob because contoso the blingblong
x("UPDATE Patrons SET Frob='Grob' WHERE AccountNumber = 619");
//WARNING: This stored procedure happens outside of any transaction!
//20110918: Dont forget we also need to bang the gardinker
x("EXECUTE BangTheGardinker @floof=619");
}
else
throw e;
}
//WARNING: This stored procedure happens outside of any transaction!
//If any error happens from
//Continue with the stuff
x("EXECUTE Frob('{498BBB4D-D9F7-4438-B7A6-4AB5D57937C0}')");
//WARNING: This stored procedure happens outside of any transaction. It will throw:
// Msg 3902, Level 16, State 1, Line 1
// The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.
//All done, commit the transaction
x("COMMIT TRANSACTION");
}
catch (Exception e)
{
//If there was an error during Frob, we would want to catch it and roll everything back.
//But since SQL Server ended the transaction, we have no way to rollback the changes
//And even if the call to Frob (or Updating the patron's Grob, or Banging the Gardinder)
//didn't fail, the call to COMMIT TRANSACTION will throw an error
//Either way, we have detected an error condition that cannot be rolled back in the database
//Something bad happened, rollback the transaction
//(if SQL Server didn't kill the transaction without our permission)
x("IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION");
throw e;
}
}
正确编写并且正常运行的代码会损坏,导致错误,最坏的情况会导致数据库损坏。所有这些都是因为我打开了XACT_ABORT ON
。
答案 5 :(得分:0)
当XACT_ABORT在触发器中设置为OFF并且我在触发器主体中调用RAISEERROR时,更改未回滚。