Ist es möglich, einen Ausführungsaufrufstapel in einem Trigger abzurufen?

16

Ich habe 10 gespeicherte Prozeduren und jede von ihnen führt INSERTs in eine tableX aus.

Ist es möglich, in einem Trigger-Body von tableX zu ermitteln, welches Objekt eine Änderung von tableX verursacht (gespeichertes proc1 oder sp2 oder ....)?

Vielen Dank.

Garik
quelle

Antworten:

9

Ja, es ist möglich, den laufenden Code mithilfe der Systemfunktion @@ procid zu identifizieren und besser OBJECT_NAME (@@ PROCID), um den vollständigen Namen zu erhalten.

Definition: Gibt die Objektkennung (ID) des aktuellen Transact-SQL-Moduls zurück. Ein Transact-SQL-Modul kann eine gespeicherte Prozedur, eine benutzerdefinierte Funktion oder ein Trigger sein. Datenzugriffsprovider verarbeiten. "

Sie können darüber lesen Sie hier .

Eine andere Möglichkeit wäre, den SQL-Plan der aktuellen Spid zu überprüfen und diese Informationen in einer Protokollierungstabelle zu speichern. Eine Beispielabfrage, die in jedem Verfahren zum Speichern von Überwachungsdaten verwendet wird, lautet:

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

Vielleicht gibt es dort zu viele Details ... aber ich glaube, dass Sie auf die Idee kommen.

Eine dritte Möglichkeit wäre, die context_info- Informationen für die aktuelle SP-Sitzung zu verwenden. Und verknüpfen Sie die dort gespeicherten Kontextinformationen mit jeder Prozedur. In procedure1 schreiben Sie beispielsweise 111 in den Kontext, in procedure2 schreiben Sie 222 .. und so weiter.

Weitere Informationen zu context_info finden Sie in dieser SO-Frage .

Marian
quelle
1
1) OBJECT_NAME (@@ PROCID) im Trigger gibt den Triggernamen zurück :(. 2) Informationen müssen nur am Trigger vorhanden sein. 3) context_info ist eine Lösung. Vielen Dank.
Garik
1
Ja, innerhalb eines Triggers wird OBJECT_NAME(@@PROCID)der Triggername zurückgegeben, nicht der aufrufende Prozess.
ProfK
Das ist einfach falsch. Es gibt den Namen des Triggers zurück, nicht den Namen der aufrufenden Prozedur, wie vom OP verlangt
Reversed Engineer
Stimmen Sie zu, die Antwort ist falsch. CONTEXT_INFO funktioniert, wenn Sie die Upstream-Prozedur ändern können.
Tom Warfield
3

Ich wollte das auch tun. Danke für die Antwort. Da ich immer noch hier bin, werde ich meinen Test posten, um anderen Zeit zu sparen :)

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
Jim Brown
quelle
2

XEvents bieten eine weitere Möglichkeit, einen T-SQL-Stapel zu ermitteln, obwohl SQL Server 2008 einen verwendeten Ereignistyp möglicherweise nicht unterstützt. Die Lösung besteht aus einem Trigger, einem Fehler und einer XEvent-Sitzung. Ich habe Jim Browns Beispiel genommen, um zu zeigen, wie es funktioniert.

Zunächst habe ich die Lösung für SQL Server 2016 SP2CU2 Dev Edition getestet. SQL Server 2008 unterstützt einige EXevent, aber ich habe keine Instanz, so dass ich es nicht testen konnte.

Die Idee ist, einen Benutzerfehler in einem Dummy-Try-Catch-Block zu generieren und den Fehler dann in einer XEvent-Sitzung mit tsql_stackaction abzufangen . SQLSERVER.error_reportedDer XEvent-Typ kann alle Fehler abfangen, obwohl sie von einem Try-Catch-Block abgefangen werden. Am Ende sys.dm_exec_sql_textextrahieren Sie T-SQL-Abfragen aus den Abfrage-Handles, die die tsql_stackAktion gibt.

Ein Beispiel aus der Antwort von Jim Brown, die ich entwickelt habe, ist unten dargestellt. Ein Trigger löst den Fehler mit dem Text 'catch me' aus. Die XEvent-Sitzung fängt Fehler nur mit dem Text 'catch me' ab.

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

Wenn Sie nun die XEvent-Sitzung starten (SSMS, Objekt-Explorer, Verwaltung, Erweiterte Ereignisse, Sitzungen, catch_insertion_into_Test), usp_RootProcIDTest ausführen und den Ringpuffer der XEvent-Sitzung prüfen, sollte das XML angezeigt werden, aus dem der Knoten besteht <action name="tsql_stack" package="sqlserver">. Es gibt eine Folge von Rahmenknoten. handleFügen Sie die Werte des Attributs von a in die Systemfunktion 'sys.dm_exec_sql_text' ein und gehen Sie wie folgt vor:

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

Ein Beispiel für einen Ausführungsaufrufstapel

Mit XEvent können Sie noch viel mehr tun! Verpassen Sie nicht die Gelegenheit, sie zu lernen!

Basil Kisel
quelle