【问题标题】:Can I exclude a stored procedure from a transaction in MS SQL?我可以从 MS SQL 中的事务中排除存储过程吗?
【发布时间】:2014-10-07 12:58:22
【问题描述】:

我的问题:我从我的业务代码中调用了一个存储过程。此调用在显式事务中。存储过程有时会调用另一个存储过程来将某些内容写入数据库。即使事务回滚,这些数据也应该保留在数据库中。一个类似的场景是当你想在日志表中写一些东西并且应该保留日志消息(这不是我的情况,它只是一个类似的要求)。

如何从外部事务中排除第二个存储过程?我认为我在 Oracle 中寻找类似“自治事务”的东西。我寻找了一个可能的仿真,但所有的解决方案看起来都不是很“好”(创建一个环回服务器,添加一些 .NET 方法,...)

有什么想法吗?谢谢!

【问题讨论】:

标签: sql-server transactions


【解决方案1】:

虽然看起来很常见,但对于这类问题没有优雅的解决方案。 begin transactioncommitrollback 之间的所有内容都是作为一个整体完成的。例如,您不能只在日志表中插入一行并在最终的 rollback 之后保留该行。

但你可以做一些技巧。

1) 使用 xp_cmdshell 调用您的过程以调用 OSQL.exe。性能会很差,但外部命令不参与事务,没有什么可以阻止您在外部执行 SQL 语句。

2) 在存储过程中,您可以将记录添加到表变量而不是真实表中。表变量不参与事务,因为它们不改变数据库。然后,当您以任何一种方式关闭事务时,将变量的内容附加到您的表中。

3) 如果您无法更改内部过程,您可以在调用之后但从仍然打开的事务中获取它可能创建的记录到表变量中。回滚事务并将获取的记录再次追加到表中。

【讨论】:

    【解决方案2】:

    是的,你可以!为自己使用链接服务器将“远程 proc 事务提升”设置为 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 次点击并不明显,但当您获得数千次点击时,您会开始看到秒数加起来 - 这并不是一种常见的做法。我们看到它远远超过了其他副作用,例如阻塞,这就是我们放置它的目的。

    【讨论】:

    • 哇,那个疯了——而且有效。好的。不知道你从哪里得到的,但很好。从来没想过:干得好。
    • 大量研究。对我们来说是个大问题。我们有容器管理的事务和我们调用的巨大存储过程(希望摆脱它)。掌握一些序列号生成的东西,这似乎可以解决问题。
    • 是的。还可以看到,从 SP 中记录调用 - 当 SP 回滚时,您可能不希望丢失日志;)很好。聪明的。说真的,查看我的个人资料——这是我难得的赞美。喜欢它
    • 感谢您的回答@Chewy。正如我在我的问题中所写的那样,我知道环回服务器,但是支持我认为根本不是异国情调的要求看起来有点“疯狂”! Oracle 有“自治事务”,所以他们也有这个要求。
    • 啊,我实际上错过了您关于环回的评论的最后一部分。我一直在阅读很多帖子试图弄清楚如何做到这一点,以至于当我回到这里时,我错过了。希望其他需要这个想法的人也能找到这个解决方案。我在甲骨文世界度过了很多时间,只是认为这很容易——天哪,我错了。当然,我是一个 java 人,spring 会为你处理这个。
    【解决方案3】:

    据我所知,您不能从包含它们的事务中排除特定操作。我的方法是尝试明确地做你想做的事情,而不是回滚整个事务。或者,如果可以选择,回滚事务,然后重新运行您不想回滚的存储过程。

    【讨论】:

    • 谢谢,但这不可行。存储过程很大,可以多次调用存储过程来存储一些信息。此信息必须保留在那里,但所有其他可能的更改都应在发生某些事情时回滚。
    猜你喜欢
    • 1970-01-01
    • 2014-11-13
    • 1970-01-01
    • 1970-01-01
    • 2023-01-06
    • 2011-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多