我的问题:我从业务代码中调用存储过程。此调用处于显式事务中。存储过程有时会调用另一个来将某些内容写入数据库。即使事务回滚,此数据也应保留在数据库中。类似的情况是当你想在日志表中写一些内容并且应该保留日志消息时(这不是我的情况,这只是一个类似的要求)。
如何从外部事务中排除第二个存储过程?我认为我正在寻找类似于"自动交易"在Oracle中我找了一个可能的模拟,但所有的解决方案都看起来不太好"很好" (创建一个环回服务器,添加一些.NET方法,......)
有什么想法吗?谢谢!
答案 0 :(得分:2)
这种类型的问题没有优雅的解决方案,尽管它似乎很常见。 begin transaction
与commit
或rollback
之间的所有内容都是完整的。例如,您不能仅仅在日志表中插入一行,并在最终rollback
后保留该行。
但你可以做一些技巧。
1)使用xp_cmdshell调用您的过程来调用OSQL.exe。性能不好,但外部命令不参与事务,没有任何东西阻止您在外部执行SQL语句。
2)在存储过程中,您可以将记录添加到表变量而不是真实表中。表变量不参与事务,因为它们不会更改数据库。然后,当您以任一方式关闭事务时,将变量的内容附加到表中。
3)如果您无法更改内部过程,则可以在调用之后将其可能创建的记录提取到表变量中,但是从仍然打开的事务中获取。回滚事务并再次将获取的记录附加到表中。
答案 1 :(得分:1)
可以!将链接服务器用于自己 ,并将“远程处理交易升级”设置为false。这是一个示例:
EXEC master.dbo.sp_addlinkedserver @server = N'LOOPBACK', @srvproduct=N'Microsoft', @provider=N'SQLNCLI', @datasrc=N'MYMACHINE\INSTANCE', @catalog=N'DB_NAME_HERE'
EXEC master.dbo.sp_serveroption @server=N'LOOPBACK', @optname=N'remote proc transaction promotion', @optvalue=N'false'
EXEC master.dbo.sp_serveroption @server=N'LOOPBACK', @optname=N'rpc', @optvalue=N'true'
EXEC master.dbo.sp_serveroption @server=N'LOOPBACK', @optname=N'rpc out', @optvalue=N'true'
--I think most below are defaults
EXEC master.dbo.sp_serveroption @server=N'LOOPBACK', @optname=N'collation compatible', @optvalue=N'false'
EXEC master.dbo.sp_serveroption @server=N'LOOPBACK', @optname=N'data access', @optvalue=N'true'
EXEC master.dbo.sp_serveroption @server=N'LOOPBACK', @optname=N'dist', @optvalue=N'false'
EXEC master.dbo.sp_serveroption @server=N'LOOPBACK', @optname=N'pub', @optvalue=N'false'
EXEC master.dbo.sp_serveroption @server=N'LOOPBACK', @optname=N'sub', @optvalue=N'false'
EXEC master.dbo.sp_serveroption @server=N'LOOPBACK', @optname=N'connect timeout', @optvalue=N'0'
EXEC master.dbo.sp_serveroption @server=N'LOOPBACK', @optname=N'collation name', @optvalue=null
EXEC master.dbo.sp_serveroption @server=N'LOOPBACK', @optname=N'lazy schema validation', @optvalue=N'false'
EXEC master.dbo.sp_serveroption @server=N'LOOPBACK', @optname=N'query timeout', @optvalue=N'0'
EXEC master.dbo.sp_serveroption @server=N'LOOPBACK', @optname=N'use remote collation', @optvalue=N'true'
CREATE PROCEDURE dbo.ap_deleteme_outsideTransaction AS
--codes is just some random table I have
exec ('insert into codes values (1, ''TEST'', ''TEST_ED'', ''DELTE_ME'', 0, ''1234'', ''myId'', getDate())') at LOOPBACK
GO
CREATE PROCEDURE dbo.ap_deleteme_test_transaction AS
begin transaction
insert into codes values (10, 'TEST', 'TEST_ED', 'DELTE_ME_1', 0, '1234', 'myId', getDate())
--exec ('generic query you may want to execute') at LOOPBACK
exec dbo.ap_deleteme_outsideTransaction
insert into codes values (20, 'TEST', 'TEST_ED', 'DELTE_ME_2', 0, '1234', 'myId', getDate())
--rolling back like this makes no sense, but here you should be able to see 3 records
--inserted and then two rolled back. The record inserted in the second proc call
--will still remain.
rollback transaction
GO
我不会将其称为最佳选择,但是一旦您设置了此链接,就应该能够将您不想参与交易的任何呼叫定向到该链接,并且该链接不会参与。我的初始测试并未显示出明显的性能下降,但请注意,循环调用可能会导致成本高昂。
-更新:性能测试表明它命中了1-2毫秒,但这比“直接”调用慢60倍。 500次左右的命中率并不明显,但是当您获得数千次命中时,您可能会开始看到秒数加起来-但这不是通常的做法。我们看到它远远超过了其他副作用,例如阻塞,这就是我们要解决的问题。
答案 2 :(得分:0)
据我所知,您不能从包含它们的事务中排除特定操作。我的方法是尝试明确地执行您想要执行的操作,而不是回滚整个事务。或者,如果它是一个选项,则回滚事务,然后重新运行您不想回滚的存储过程。