这是一个奇怪的问题:有没有办法让回滚豁免插入到表中?
场景如下:我们有一个做事的触发器。
有时,这个触发器会调用RAISERRROR()
. 并且外部事务回滚。
但是,在触发器中,我想将值插入到日志记录表中,并且在回滚期间不让它消失,如果你愿意的话,这是一个事务豁免插入。
这是一个奇怪的问题:有没有办法让回滚豁免插入到表中?
场景如下:我们有一个做事的触发器。
有时,这个触发器会调用RAISERRROR()
. 并且外部事务回滚。
但是,在触发器中,我想将值插入到日志记录表中,并且在回滚期间不让它消失,如果你愿意的话,这是一个事务豁免插入。
您可以捕获异常try-catch
并将它们传递/抛出到外部范围,其中必须存在相同的东西。顺便说一句,这可以让您收集call-stack
. 如果您仅通过存储过程工作,则可以实施此解决方案。每个 proc 都必须有这样的模式:
begin try
end try
begin catch
if @@trancount > 0
rollback
insert into <log> (...)
values (...)
throw
end catch
所以最上面的过程将成功地在日志表中插入一行。
缺点:
优点:
回馈:
您可以构建一个插入日志表的 CLR 程序集,仅此而已……但是!可以指定一个单独的连接,使这个程序集通过这个单独的连接与 db 一起工作。这意味着 - 在单独的范围内。所以这个程序集的方法是从一个事务中被调用的,但是不管这个事务范围是被执行的。
所以,而不是:
using(SqlConnection connection = new SqlConnection("context connection=true"))
只需指定一个常规连接字符串。调用它后 - 抛出异常并修改ERROR_STATUS
以避免额外记录相同的错误。
begin try
end try
begin catch
if @@trancount > 0
rollback
if @@ERRROR_STATUS != @done_with_logging
exec asm.log(...)
raiserror @err_msg, @severity, @done_with_logging
end catch
缺点:
优点:
回馈:
这个简单的语句(实际上只是附加选项WITH LOG
)会将您想要的任何错误消息写入 SQL SERVER 事件日志:
RAISERROR(...) WITH LOG
这不是应该使用 SQL SERVER 日志的方式,但这是记录重要内容(用于解决问题)的最快方式。记录的事件可以在 SSMS 代理的窗口中查看。
缺点:
优点:
回馈:
{基于 DML 触发器构建系统的基于意见的讨论的地方}
在我看来,您没有在项目中使用存储过程,而是执行临时查询。如果你有一个带有 ORM 或类似的后端应用程序 - 用它写日志。此外,也许这个后端应用程序是一个更好的地方来做你在那个触发器中做的事情。
如果您的项目是没有应用程序服务器/后端应用程序的客户端-服务器应用程序,而您得到的只是一个临时查询和触发器,那么需要记录的数据不多。没有调用堆栈(在服务器端)。而且很难确定您(用户、应用程序)是如何遇到这个特殊异常的。因此,在这种情况下,登录客户端可能更有用。
如果您创建一个表变量然后插入其中 - 这将不会包含在回滚中,因此您可以将内容转储到永久表中。
例如
declare @tab table (msg varchar(255))
BEGIN TRY
BEGIN TRANSACTION
select 1+2
INSERT @tab values ('first step complete')
SElect 1/0
INSERT @tab values ('2nd step complete')
COMMIT
END TRY
BEGIN CATCH
ROLLBACK
SELECT * FROM @tab
END CATCH
过段时间再写。您可以使用代替触发器:
create table t
(
i int,
s varchar(10)
)
go
create table t2
(
i int,
s varchar(10)
)
go
create table tlog
(
i int,
s varchar(10)
)
go
alter trigger tt on t
INSTEAD OF INSERT
AS
BEGIN
rollback transaction
raiserror ('something went wrong', 16, 2)
insert tlog (i,s)
select i, s
from inserted
END
go
truncate table t
truncate table t2
truncate table tlog
go
select * from t
select * from t2
select * from tlog
go
begin transaction
insert t2 (i,s) values (1, 'abc')
insert t (i,s) values (1, 'abc')
commit transaction
go
select * from t
select * from t2
select * from tlog
给出以下输出:
是
(0 行受影响)
是
(0 行受影响)
是
(0 行受影响)
(1 行受影响)消息 50000,级别 16,状态 2,程序 tt,第 7 行出现问题
(1 行受影响) Msg 3609, Level 16, State 1, Line 4 事务在触发器中结束。该批次已中止。是
(0 行受影响)
是
(0 行受影响)
是
1个ABC
(1 行受影响)