在当前事务之外提交事务(如Oracle中的自治事务)

时间:2017-08-17 09:21:23

标签: sql sql-server tsql stored-procedures transactions

我需要从存储过程写入日志表。 现在这个日志信息必须能够在回滚的情况下继续存在。

我知道之前已经问过这个问题,但我的情况有所不同,我在这些问题中找不到问题的答案。

当存储过程中没有错误时,事情很简单,logtable中的条目就在那里 当出现错误而不是复杂的事情时 在程序中,我可以在catch中进行回滚,然后将数据插入到日志表中,我知道并且我已经这样做了。
但问题是当这样调用存储过程时:

begin transaction
exec myStoredProcedure
rollback transaction
select * from myLogTable

我知道这段代码没有多大意义,我保持它最小化以证明我的问题 如果存储过程的调用者进行提交/回滚,那么我在存储过程中所做的事情并不重要。我的爱好总是会回滚的。

我也不能使用临时表技巧,即返回我想要记录的数据,并让调用者在完成回滚后使用该数据将其插入到日志表中,因为调用者是一个外部应用程序,我没有来源。

日志记录是在一个单独的过程中完成的,该过程只有一行代码,插入到logtable中 我需要的是一种在当前事务之外提交此过程中的插入的方法,以便它可以在任何回滚中存活。

有办法做到这一点吗?

解决方案:

我使用lad2025回答,到目前为止,它没有任何问题或性能问题 但是这个程序每天只会调用大约1000次而不是那么多,所以我想我也不必指望任何问题。

2 个答案:

答案 0 :(得分:2)

所以你真的要搜索某种 Autonomous transaction (就像在Oracle中一样)。

模拟它的一种丑陋方法是使用环回链接服务器。

警告:这是PoC(我会在PROD中使用它之前会三思而后行)并进行大量测试。

DECLARE @servername SYSNAME;
SET     @servername = CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName'));

EXECUTE sys.sp_addlinkedserver
            @server = N'loopback', 
            @srvproduct = N'', 
            @provider = N'SQLNCLI', 
            @datasrc = @servername;

EXECUTE sys.sp_serveroption 
            @server = N'loopback', 
            @optname = 'RPC OUT', 
            @optvalue = 'ON';

EXECUTE sys.sp_serveroption 
        @server = N'loopback', 
        @optname = 'remote proc transaction promotion',
        @optvalue = 'OFF';

代码:

DROP TABLE IF EXISTS myLogTable;
CREATE TABLE myLogTable(i INT IDENTITY(1,1),
                        d DATETIME DEFAULT GETDATE(),
                        t NVARCHAR(1000));
GO

CREATE OR ALTER PROCEDURE my_logging
   @t NVARCHAR(MAX)
AS
BEGIN
   INSERT INTO myLogTable(t) VALUES (@t);
END;
GO

CREATE OR ALTER PROCEDURE myStoredProcedure
AS
BEGIN
    -- some work
    SELECT 1;
    INSERT INTO myLogTable(t) 
    VALUES ('Standard logging that will perish after rollback');

    EXEC loopback.T1.dbo.my_logging 
       @t = N'some custom loging that should survive rollback';
END;

最后的电话:

begin transaction
exec myStoredProcedure
rollback transaction
select * from myLogTable

输出:

i   d          t
2   2017-08-17 some custom loging that should survive rollback

答案 1 :(得分:2)

这是一个非常有趣的主题,所以让我们来看看MS如何接近它。

第一份文件:Migrating-Oracle-to-SQL-Server-2014-and-Azure-SQL-DB.pdf

  

第152页。

     

模拟Oracle自治交易

     

本节介绍SSMA for Oracle V6.0如何处理自治事务   (PRAGMA AUTONOMOUS_TRANSACTION)。这些自主交易没有   在Microsoft SQL Server 2014中具有直接等效功能。

     

定义PL / SQL块时(匿名块,过程,函数,打包)   程序,打包函数,数据库触发器)作为自治事务,你   将该块中的DML与调用者的事务上下文隔离开来。块变成了   由另一个事务启动的独立事务,称为主事务   事务。

     

要将PL / SQL块标记为自治事务,只需包含   声明部分中的以下声明:   PRAGMA AUTONOMOUS_TRANSACTION;

     

SQL Server 2014不支持自治事务。孤立的唯一方法   来自事务上下文的Transact-SQL块将打开一个新连接。

     

使用 xp_ora2ms_exec2 扩展程序及其扩展版本    xp_ora2ms_exec2_ex ,与SSMA 6.0 Extension Pack捆绑在一起,以打开新的   交易。该过程的目的是调用新的任何存储过程   连接和帮助调用函数体中的存储过程。该   xp_ora2ms_exec2过程具有以下语法:

xp_ora2ms_exec2
<active_spid> int,
<login_time> datetime,
<ms_db_name> varchar,
<ms_schema_name> varchar,
<ms_procedure_name> varchar,
<bind_to_transaction_flag> varchar,
[optional_parameters_for_procedure];

然后您需要在您的服务器上安装存储过程和其他脚本: SSMA for Oracle Extension Pack(仅适用于Oracle Extension Pack.7.5.0.msi的SSMA)。

您的存储过程将变为:

CREATE TABLE myLogTable(i INT IDENTITY(1,1),
                        d DATETIME DEFAULT GETDATE(),
                        t NVARCHAR(1000));
GO

CREATE OR ALTER PROCEDURE my_logging
   @t NVARCHAR(MAX)
AS
BEGIN
   INSERT INTO myLogTable(t) VALUES (@t);
END;
GO

CREATE OR ALTER PROCEDURE myStoredProcedure
AS
BEGIN
    -- some work
    SELECT 1;
    INSERT INTO myLogTable(t) 
    VALUES ('Standard logging that will perish after rollback');

    DECLARE @login_time DATETIME = GETDATE();
    DECLARE @custom_text_to_log NVARCHAR(100);
    SET @custom_text_to_log=N'some custom loging that should survive rollback';
    DECLARE @database_name SYSNAME = DB_NAME();

    EXEC master.dbo.xp_ora2ms_exec2_ex 
       @@spid,
       @login_time,
       @database_name,
       'dbo',
       'my_logging',
       'N',
       @custom_text_to_log;
END;

最后的电话:

begin transaction
exec myStoredProcedure
rollback transaction
select * from myLogTable;

输出:

i   d          t
2   2017-08-21 some custom loging that should survive rollback