SQL 服务器 XACT_ABORT 排除
SQL Server XACT_ABORT with exclusion
我有一个更大的存储过程,它利用几个 TRY/CATCH 块来捕获和记录单个错误。我还围绕过程的全部内容包装了一个事务,以便能够在沿途某处出现错误的情况下回滚整个事情(为了防止大量混乱的清理); XACT_ABORT 已启用,否则它不会回滚整个事务。
关键组成部分:
我的数据库中有一个 table,每次此过程 运行 时都会插入一条记录,其中包含操作结果和出错的详细信息。
有趣的事情正在发生——实际上,当我终于弄清楚出了什么问题时,很明显……我日志中的插入语句 table 也被回滚了,因此,如果我不是 运行 将其从 SSMS 中删除,我将无法看到这甚至是 运行,因为回滚删除了 activity.
的所有恍惚
问题:
除了这个插入语句之外,是否有可能让整个事务回滚?我仍然想保留在存储过程 运行 期间编译的错误消息。
非常感谢!
~以利
6 月 28 日更新
这是我正在查看的代码示例。这与@Alex 和@gameiswar 提出的样本之间的主要区别在于,在我的例子中,try/catch 块都嵌套在单个事务中。这样做的目的是有多个捕获(对于多个 tables),尽管即使上次更新失败我们也会回滚整个混乱。
SET XACT_ABORT ON;
BEGIN TRANSACTION
DECLARE @message AS VARCHAR(MAX) = '';
-- TABLE 1
BEGIN TRY
UPDATE TABLE xx
SET yy = zz
END TRY
BEGIN CATCH
SET @message = 'TABLE 1 '+ ERROR_MESSAGE();
INSERT INTO LOGTABLE
SELECT
GETDATE(),
@message
RETURN;
END CATCH
-- TABLE 2
BEGIN TRY
UPDATE TABLE sss
SET tt = xyz
END TRY
BEGIN CATCH
SET @message = 'TABLE 2 '+ ERROR_MESSAGE();
INSERT INTO LOGTABLE
SELECT
GETDATE(),
@message
RETURN;
END CATCH
COMMIT TRANSACTION
我不知道细节,但恕我直言,一般逻辑可能是这样的。
--set XACT_ABORT ON --not include it
declare @result varchar(max) --collect details in case you need it
begin transaction
begin try
--your logic here
--if something wrong RAISERROR(...@result)
--everything OK
commit
end try
begin catch
--collect error_message() and other into @result
rollback
end catch
insert log(result) values (@result)
您可以尝试类似下面的方法,这样可以确保您记录 operation.This 利用了 table 变量不会回滚的事实..
伪代码仅供参考:
create table test1
(
id int primary key
)
create table logg
(
errmsg varchar(max)
)
declare @errmsg varchar(max)
set xact_abort on
begin try
begin tran
insert into test1
select 1
insert into test1
select 1
commit
end try
begin catch
set @errmsg=ERROR_MESSAGE()
select @errmsg as "in block"
if @@trancount>0
rollback tran
end catch
set xact_abort off
select @errmsg as "after block";
insert into logg
select @errmsg
select * from logg
好的...我结合了 Alex 和 GameisWar 提出的很好的建议,加上 T-SQL GOTO 控制流语句,解决了这个问题。
基本想法是将错误消息存储在变量中,该变量在回滚后仍然存在,然后让 Catch 将您发送到 FAILURE 标签,该标签将执行以下操作:
- 回滚事务
- 使用上述变量
中的数据向日志中插入一条记录table
- 退出存储过程
我还使用第二个 GOTO 语句来确保成功 运行 将跳过 FAILURE 部分并提交事务。
下面是测试 SQL 的代码片段。它就像一个魅力,我已经实现了它并在我们的生产环境中(成功地)测试了它。
非常感谢所有的帮助和意见!
SET XACT_ABORT ON
DECLARE @MESSAGE VARCHAR(MAX) = '';
BEGIN TRANSACTION
BEGIN TRY
INSERT INTO TEST_TABLE VALUES ('TEST'); -- WORKS FINE
END TRY
BEGIN CATCH
SET @MESSAGE = 'ERROR - SECTION 1: ' + ERROR_MESSAGE();
GOTO FAILURE;
END CATCH
BEGIN TRY
INSERT INTO TEST_TABLE VALUES ('TEST2'); --WORKS FINE
INSERT INTO TEST_TABLE VALUES ('ANOTHER TEST'); -- ERRORS OUT, DATA WOULD BE TRUNCATED
END TRY
BEGIN CATCH
SET @MESSAGE = 'ERROR - SECTION 2: ' + ERROR_MESSAGE();
GOTO FAILURE;
END CATCH
GOTO SUCCESS;
FAILURE:
ROLLBACK
INSERT INTO LOGG SELECT @MESSAGE
RETURN;
SUCCESS:
COMMIT TRANSACTION
我有一个更大的存储过程,它利用几个 TRY/CATCH 块来捕获和记录单个错误。我还围绕过程的全部内容包装了一个事务,以便能够在沿途某处出现错误的情况下回滚整个事情(为了防止大量混乱的清理); XACT_ABORT 已启用,否则它不会回滚整个事务。
关键组成部分:
我的数据库中有一个 table,每次此过程 运行 时都会插入一条记录,其中包含操作结果和出错的详细信息。
有趣的事情正在发生——实际上,当我终于弄清楚出了什么问题时,很明显……我日志中的插入语句 table 也被回滚了,因此,如果我不是 运行 将其从 SSMS 中删除,我将无法看到这甚至是 运行,因为回滚删除了 activity.
的所有恍惚问题:
除了这个插入语句之外,是否有可能让整个事务回滚?我仍然想保留在存储过程 运行 期间编译的错误消息。
非常感谢!
~以利
6 月 28 日更新
这是我正在查看的代码示例。这与@Alex 和@gameiswar 提出的样本之间的主要区别在于,在我的例子中,try/catch 块都嵌套在单个事务中。这样做的目的是有多个捕获(对于多个 tables),尽管即使上次更新失败我们也会回滚整个混乱。
SET XACT_ABORT ON;
BEGIN TRANSACTION
DECLARE @message AS VARCHAR(MAX) = '';
-- TABLE 1
BEGIN TRY
UPDATE TABLE xx
SET yy = zz
END TRY
BEGIN CATCH
SET @message = 'TABLE 1 '+ ERROR_MESSAGE();
INSERT INTO LOGTABLE
SELECT
GETDATE(),
@message
RETURN;
END CATCH
-- TABLE 2
BEGIN TRY
UPDATE TABLE sss
SET tt = xyz
END TRY
BEGIN CATCH
SET @message = 'TABLE 2 '+ ERROR_MESSAGE();
INSERT INTO LOGTABLE
SELECT
GETDATE(),
@message
RETURN;
END CATCH
COMMIT TRANSACTION
我不知道细节,但恕我直言,一般逻辑可能是这样的。
--set XACT_ABORT ON --not include it
declare @result varchar(max) --collect details in case you need it
begin transaction
begin try
--your logic here
--if something wrong RAISERROR(...@result)
--everything OK
commit
end try
begin catch
--collect error_message() and other into @result
rollback
end catch
insert log(result) values (@result)
您可以尝试类似下面的方法,这样可以确保您记录 operation.This 利用了 table 变量不会回滚的事实..
伪代码仅供参考:
create table test1
(
id int primary key
)
create table logg
(
errmsg varchar(max)
)
declare @errmsg varchar(max)
set xact_abort on
begin try
begin tran
insert into test1
select 1
insert into test1
select 1
commit
end try
begin catch
set @errmsg=ERROR_MESSAGE()
select @errmsg as "in block"
if @@trancount>0
rollback tran
end catch
set xact_abort off
select @errmsg as "after block";
insert into logg
select @errmsg
select * from logg
好的...我结合了 Alex 和 GameisWar 提出的很好的建议,加上 T-SQL GOTO 控制流语句,解决了这个问题。
基本想法是将错误消息存储在变量中,该变量在回滚后仍然存在,然后让 Catch 将您发送到 FAILURE 标签,该标签将执行以下操作:
- 回滚事务
- 使用上述变量 中的数据向日志中插入一条记录table
- 退出存储过程
我还使用第二个 GOTO 语句来确保成功 运行 将跳过 FAILURE 部分并提交事务。
下面是测试 SQL 的代码片段。它就像一个魅力,我已经实现了它并在我们的生产环境中(成功地)测试了它。
非常感谢所有的帮助和意见!
SET XACT_ABORT ON
DECLARE @MESSAGE VARCHAR(MAX) = '';
BEGIN TRANSACTION
BEGIN TRY
INSERT INTO TEST_TABLE VALUES ('TEST'); -- WORKS FINE
END TRY
BEGIN CATCH
SET @MESSAGE = 'ERROR - SECTION 1: ' + ERROR_MESSAGE();
GOTO FAILURE;
END CATCH
BEGIN TRY
INSERT INTO TEST_TABLE VALUES ('TEST2'); --WORKS FINE
INSERT INTO TEST_TABLE VALUES ('ANOTHER TEST'); -- ERRORS OUT, DATA WOULD BE TRUNCATED
END TRY
BEGIN CATCH
SET @MESSAGE = 'ERROR - SECTION 2: ' + ERROR_MESSAGE();
GOTO FAILURE;
END CATCH
GOTO SUCCESS;
FAILURE:
ROLLBACK
INSERT INTO LOGG SELECT @MESSAGE
RETURN;
SUCCESS:
COMMIT TRANSACTION