是否可以在触发器中获取执行调用堆栈?


16

我有10个存储过程,每个存储过程都将INSERTs插入一个tableX中。

在tableX的触发器主体中是否有可能获取导致tableX修改的对象(存储的proc1或sp2或....)?

谢谢。

Answers:


9

是的,可以使用@@ procid系统函数和更好的OBJECT_NAME(@@ PROCID)来标识正在运行的代码,以使其具有完整的名称。

定义:“返回当前Transact-SQL模块的对象标识符(ID)。Transact-SQL模块可以是存储过程,用户定义的函数或触发器。@@ PROCID不能在CLR模块或in-处理数据访问提供程序。”

你可以在这里阅读。

另一个选择是检查当前spid的sql计划并将该信息保存在日志表中。每个过程中用于保存审核数据的示例查询为:

select sp.hostname, sp.program_name, sp.loginame,
    st.text as query_text
from sysprocesses sp
cross apply sys.dm_exec_sql_text(sp.sql_handle) as st  
where sp.spid = @@spid

也许那里有太多细节。.但是我相信你明白了。

第三种选择是context_info信息用于当前SP的会话。并将与每个过程一起保存的上下文信息关联到某个地方。例如,在procedure1中,您为上下文编写了111;在procedure2中,您编写了222 ..,依此类推。

您可以在此SO问题中阅读有关context_info的更多信息。


1
1)触发器中的OBJECT_NAME(@@ PROCID)返回触发器名称:(。2)必须在触发器处具有信息。3)context_info是一种解决方案。谢谢。
加里克2011年

1
是的,在触发器内部将OBJECT_NAME(@@PROCID)返回触发器名称,而不是调用proc。
ProfK

这是完全错误的。它返回触发器的名称,而不是OP要求的调用过程的名称
反向工程师,

同意,答案是错误的。如果可以修改上游过程,则CONTEXT_INFO将起作用。
汤姆·沃菲尔德,

3

我也想这样做。感谢您的回答。当我还在这里时,我将发布测试以节省其他人的时间:)

CREATE TABLE  Test ( TestID INT )
GO

CREATE TRIGGER TestTrigger ON Test
FOR INSERT
AS

SELECT CAST(CONTEXT_INFO() AS NVARCHAR(128));
GO

CREATE PROCEDURE usp_ProcIDTest
AS

DECLARE @ProcedureName VARBINARY(MAX) = CAST(OBJECT_NAME(@@PROCID) AS VARBINARY(MAX))
SET CONTEXT_INFO @ProcedureName

INSERT INTO Test ( TestID ) VALUES ( 1 ) 

GO

EXEC usp_ProcIDTest
GO

DROP TABLE Test
GO

2

XEvents提供了另一种获取T-SQL堆栈的方法,尽管SQL Server 2008可能不支持使用的事件类型。该解决方案包括触发器,错误和XEvent会话。我以吉姆·布朗(Jim Brown)的例子来说明它的工作方式。

首先,我测试了SQL Server 2016 SP2CU2 Dev Edition的解决方案。SQL Server 2008支持某些EXevent,但是我没有任何实例,因此无法对其进行测试。

想法是在虚拟try-catch块中生成用户错误,然后使用tsql_stack操作在XEvent会话中捕获错误。SQLSERVER.error_reportedXEvent类型可以捕获所有错误,即使try-catch块可以捕获它们。最后,sys.dm_exec_sql_text从提供操作的查询句柄中提取T-SQL查询tsql_stack

下面显示了我开发的Jim Brown答案中的一个示例。触发器会引发错误,提示文字为“抓住我”。XEvent会话仅使用“ catch me”之类的文本捕获错误。

CREATE TABLE  Test ( TestID INT )

GO

CREATE TRIGGER TestTrigger ON Test
FOR INSERT
AS
BEGIN TRY
    SET XACT_ABORT OFF; -- REALLY IMPORTANT!
    /* make an catching a great deal more interesting */
    DECLARE @TestID NVARCHAR(MAX) ;
    SELECT TOP (1) @TestID = CAST(ins.TestID AS NVARCHAR(MAX)) FROM inserted AS ins ;
    RAISERROR (N'catch_me TestID = "%s"' , 11 , 0 , @TestID) ;
END TRY BEGIN CATCH /* NOTHING TO DO */ END CATCH

GO

CREATE PROCEDURE usp_ProcIDTest
AS
INSERT INTO Test ( TestID ) VALUES ( 1 ) 

GO

CREATE PROCEDURE usp_RootProcIDTest
AS
EXEC usp_ProcIDTest

GO

-- This XEvent session definition was kindly provided by XEvent 'New Session' wizard.
CREATE EVENT SESSION [catch_insertion_into_Test] ON SERVER 
ADD EVENT sqlserver.error_reported(
    ACTION(package0.callstack,sqlserver.client_app_name,sqlserver.client_hostname,sqlserver.client_pid,sqlserver.database_id,sqlserver.query_hash,sqlserver.query_plan_hash,sqlserver.server_principal_name,sqlserver.session_id,sqlserver.session_nt_username,sqlserver.sql_text,sqlserver.tsql_frame,sqlserver.tsql_stack,sqlserver.username,sqlserver.context_info,sqlserver.plan_handle)
    WHERE ([message] like N'catch_me%'))
ADD TARGET package0.ring_buffer(SET max_memory=(10240))
WITH (MAX_MEMORY=4096 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,MAX_DISPATCH_LATENCY=30 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=NONE,TRACK_CAUSALITY=OFF,STARTUP_STATE=ON)

GO

现在,如果启动XEvent会话(SSMS,对象资源管理器,管理,扩展事件,会话,catch_insertion_into_Test),执行usp_RootProcIDTest并查看XEvent会话的环形缓冲区,您将看到包含该节点的XML <action name="tsql_stack" package="sqlserver">。有一系列帧节点。将handle'属性的值放入系统函数'sys.dm_exec_sql_text'中,然后添加:

-- REPLACE MY HANDLES WITH YOURS
SELECT * FROM sys.dm_exec_sql_text(0x03000800D153096910272C01A6AA000000000000000000000000000000000000000000000000000000000000);
SELECT * FROM sys.dm_exec_sql_text(0x030008000A78FD6912272C01A6AA000001000000000000000000000000000000000000000000000000000000);
SELECT * FROM sys.dm_exec_sql_text(0x03000800439CF16A13272C01A6AA000001000000000000000000000000000000000000000000000000000000);

执行调用堆栈示例

XEvent不仅可以让您做更多的事情!不要错过学习它们的机会!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.