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

标签 sql sql-server t-sql stored-procedures transactions

我需要从存储过程写入日志表。 现在,此日志信息必须在回滚过程中幸存下来。

我知道这个问题以前曾被问过,但我的情况不同,我无法在这些问题中找到我的问题的答案。

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

begin transaction
exec myStoredProcedure
rollback transaction
select * from myLogTable

我知道这段代码没有多大意义,我尽量简化它来演示我的问题。
如果存储过程的调用者执行提交/回滚,那么我在存储过程中做什么并不重要。我的登录记录将永远被回滚。

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

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

有办法做到这一点吗?

解决方案:

我使用了 lad2025 答案,到目前为止它的工作没有任何问题或性能问题。
但这个过程每天只会被调用大约 1000 次,这并不算多,所以我想我也不必期待任何问题。

最佳答案

这是一个非常有趣的话题,所以让我们看看 MS 是如何处理它的。

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

Page 152.

Simulating Oracle Autonomous Transactions

This section describes how SSMA for Oracle V6.0 handles autonomous transactions (PRAGMA AUTONOMOUS_TRANSACTION). These autonomous transactions do not have direct equivalents in Microsoft SQL Server 2014.

When you define a PL/SQL block (anonymous block, procedure, function, packaged procedure, packaged function, database trigger) as an autonomous transaction, you isolate the DML in that block from the caller's transaction context. The block becomes an independent transaction started by another transaction, referred to as the main transaction.

To mark a PL/SQL block as an autonomous transaction, you simply include the following statement in your declaration section: PRAGMA AUTONOMOUS_TRANSACTION;

SQL Server 2014 does not support autonomous transactions. The only way to isolate a Transact-SQL block from a transaction context is to open a new connection.

Use the xp_ora2ms_exec2 extended procedure and its extended version xp_ora2ms_exec2_ex, bundled with the SSMA 6.0 Extension Pack, to open new transactions. The procedure's purpose is to invoke any stored procedure in a new connection and help invoke a stored procedure within a function body. The xp_ora2ms_exec2 procedure has the following syntax:

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

关于sql - 在当前事务之外提交事务(如 Oracle 中的自治事务),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45731207/

相关文章:

SQL Server 2012 - 根据另一列的值重置运行总计

sql - 将缺失的信息添加到表中? (考虑随机开始和结束月份)

mysql - 合并找到的结果和未找到的结果

sql-server - 从命令行迁移 Entity Framework

sql-server - 如何从动态执行的查询中设置/获取/返回参数

sql - 查询变量表而不存储变量

mysql - Oracle 语法 & 在自反关系中选择最大和最小行

c# - 在 C# 中将 SQL 查询(带有相关子查询)转换为 LINQ

sql-server - GUID 可能发生冲突吗?

c# - 根据特定信息为数据库生成 id