¿Es posible obtener la pila de llamadas de ejecución en un disparador?


16

Tengo 10 procedimientos almacenados y cada uno de ellos INSERTA en una tablaX.

¿Es posible en un cuerpo de activación de tableX obtener qué objeto causa la modificación de tableX (proc1 almacenado o sp2 o ...)?

Gracias.

Respuestas:


9

Sí, es posible identificar el código en ejecución, utilizando la función de sistema @@ procid , y mejor OBJECT_NAME (@@ PROCID) para tener el nombre completo.

Definición: "Devuelve el identificador de objeto (ID) del módulo Transact-SQL actual. Un módulo Transact-SQL puede ser un procedimiento almacenado, una función definida por el usuario o un disparador. @@ PROCID no se puede especificar en los módulos CLR o en el proveedor de acceso a datos de proceso ".

Puedes leer sobre esto aquí .

Otra opción sería verificar el plan sql del spid actual y guardar esa información en una tabla de registro. Una consulta de muestra que se utilizará en cada procedimiento para guardar datos de auditoría sería:

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

Tal vez hay demasiados detalles allí ... pero creo que entiendes la idea.

Una tercera opción sería utilizar la información context_info para la sesión del SP actual. Y asocie en algún lugar la información de contexto guardada allí con cada procedimiento. Por ejemplo, en el procedimiento 1, escribe 111 en el contexto, en el procedimiento 2, escribe 222 ... y así sucesivamente.

Puede leer mucha más información sobre context_info en esta pregunta SO .


1
1) OBJECT_NAME (@@ PROCID) en el activador devuelve el nombre del activador :(. 2) es necesario tener información solo en el activador. 3) context_info es una solución. Gracias.
garik

1
Sí, dentro de un disparador OBJECT_NAME(@@PROCID)devuelve el nombre del disparador, no el proceso de llamada.
ProfK

Esto esta simplemente mal. Devuelve el nombre del disparador, no del procedimiento de llamada como solicitó el OP
ingeniero revertido el

De acuerdo, la respuesta es incorrecta. CONTEXT_INFO funciona si puede modificar el procedimiento ascendente.
Tom Warfield

3

Yo también quería hacer esto. Gracias por la respuesta. Como todavía estoy aquí, publicaré mi prueba para ahorrar tiempo a otros :)

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 proporciona otra forma de dar a conocer una pila T-SQL aunque SQL Server 2008 podría no admitir un tipo de evento usado. La solución consiste en un disparador, un error y una sesión XEvent. Tomé el ejemplo de Jim Brown para mostrar cómo funciona.

En primer lugar, probé la solución para SQL Server 2016 SP2CU2 Dev Edition. SQL Server 2008 admite algunos EXevent, pero no tengo ninguna instancia, por lo que no pude probarlo.

La idea es generar un error de usuario en un bloque ficticio try-catch, luego detectar el error dentro de una sesión XEvent con tsql_stackacción. SQLSERVER.error_reportedEl tipo XEvent puede detectar todos los errores aunque un bloque try-catch los atrape. Al final, sys.dm_exec_sql_textextraiga las consultas T-SQL de los controles de consulta que tsql_stackda la acción.

A continuación se muestra un ejemplo de la respuesta de Jim Brown, que desarrollé. Un disparador genera el error con el texto 'atraparme'. La sesión de XEvent detecta errores solo con el texto como "Atrápame".

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

Ahora, si inicia la sesión XEvent (SSMS, Object Explorer, Management, Extended Events, Sessions, catch_insertion_into_Test), ejecute usp_RootProcIDTest y mire el búfer en anillo de la sesión XEvent, debería ver el XML que consiste en el nodo <action name="tsql_stack" package="sqlserver">. Hay una secuencia de nodos de trama. Ponga los valores de un handleatributo 's en la función del sistema' sys.dm_exec_sql_text ', y voilà:

-- 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);

Un ejemplo de pila de llamadas de ejecución

¡XEvent te permite hacer mucho más que esto! ¡No pierdas las oportunidades de aprenderlas!

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.