我需要从存储过程写入日志表。 现在这个日志信息必须能够在回滚的情况下继续存在。
我知道之前已经问过这个问题,但我的情况有所不同,我在这些问题中找不到问题的答案。
当存储过程中没有错误时,事情很简单,logtable中的条目就在那里
当出现错误而不是复杂的事情时
在程序中,我可以在catch中进行回滚,然后将数据插入到日志表中,我知道并且我已经这样做了。
但问题是当这样调用存储过程时:
begin transaction
exec myStoredProcedure
rollback transaction
select * from myLogTable
我知道这段代码没有多大意义,我保持它最小化以证明我的问题 如果存储过程的调用者进行提交/回滚,那么我在存储过程中所做的事情并不重要。我的爱好总是会回滚的。
我也不能使用临时表技巧,即返回我想要记录的数据,并让调用者在完成回滚后使用该数据将其插入到日志表中,因为调用者是一个外部应用程序,我没有来源。
日志记录是在一个单独的过程中完成的,该过程只有一行代码,插入到logtable中 我需要的是一种在当前事务之外提交此过程中的插入的方法,以便它可以在任何回滚中存活。
有办法做到这一点吗?
解决方案:
我使用lad2025
回答,到目前为止,它没有任何问题或性能问题
但是这个程序每天只会调用大约1000次而不是那么多,所以我想我也不必指望任何问题。
答案 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