我正在试图找出更新列值的内容,而我对应用程序知之甚少.快速浏览一下,我注意到大约90%的应用程序业务逻辑都是在数据库上处理的.毋庸置疑,SP,功能和触发器的深度是疯狂的.
我想在有问题的表上创建一个触发器,它将记录影响该表的sql.可以使用什么sql在正在更新的表的上下文中获取已执行的sql?
细节:
MS sql Server 2008
谢谢!!
解决方法
我意识到这个问题已经解决,但我对如何使用sql Server 2008扩展事件解决它感兴趣.这是我第一次使用XEvents,所以我相信还有很多需要改进的地方!
设置测试数据库的脚本
CREATE TABLE [dbo].[TableWithMysteryUpdate]( [Period] [int] NOT NULL,[ColumnThatWillBeUpdated] [int] NOT NULL ) ON [PRIMARY] GO INSERT [dbo].[TableWithMysteryUpdate] ([Period],[ColumnThatWillBeUpdated]) VALUES (1,20) INSERT [dbo].[TableWithMysteryUpdate] ([Period],[ColumnThatWillBeUpdated]) VALUES (2,23) GO CREATE TABLE [dbo].[TestTable]( [foo] [int] IDENTITY(1,1) NOT NULL,[bar] [nchar](10) NOT NULL,CONSTRAINT [PK_TestTable] PRIMARY KEY CLUSTERED ( [foo] ASC ) ) GO CREATE TRIGGER [dbo].[triggerCausingMysteryUpdate] ON [dbo].[TestTable] AFTER INSERT AS BEGIN SET NOCOUNT ON; UPDATE [dbo].[TableWithMysteryUpdate] SET [Period] = [Period]+1 END GO CREATE PROCEDURE [dbo].[Proc4] AS BEGIN INSERT INTO [dbo].[TestTable] ( [bar]) VALUES ('Test') END GO CREATE PROCEDURE [dbo].[Proc3] AS BEGIN EXEC dbo.Proc4 END GO CREATE PROCEDURE [dbo].[Proc2] AS BEGIN EXEC dbo.Proc3 END GO CREATE PROCEDURE [dbo].[Proc1] AS BEGIN EXEC dbo.Proc2 END
所以场景是TableWithMysteryUpdate正在更新,但我不确定是什么.我将添加一个不执行任何操作的更新触发器,以便能够对此对象进行过滤.
CREATE TRIGGER [dbo].[triggerAfterUpdate] ON [dbo].[TableWithMysteryUpdate] AFTER UPDATE AS BEGIN SET NOCOUNT ON; END
然后运行脚本以创建XEvents会话,触发最终将调用堆栈导致更新发生的过程,然后停止会话.
USE TestDB DECLARE @Dynsql nvarchar(max) SET @Dynsql = ' IF EXISTS(SELECT * FROM sys.server_event_sessions WHERE name=''test_trace'') DROP EVENT SESSION [test_trace] ON SERVER; CREATE EVENT SESSION [test_trace] ON SERVER ADD EVENT sqlserver.sql_statement_completed( ACTION (package0.callstack,sqlserver.session_id,sqlserver.sql_text,sqlserver.tsql_stack) WHERE (object_id = ' + cast(object_id('[dbo].[triggerAfterUpdate]') as varchar(10)) + ') ),ADD EVENT sqlserver.sp_statement_completed( ACTION (package0.callstack,sqlserver.tsql_stack) WHERE (object_id = ' + cast(object_id('[dbo].[triggerAfterUpdate]') as varchar(10)) + ') ) ADD TARGET package0.asynchronous_file_target (set filename = ''c:\temp\test_trace.xel'',Metadatafile = ''c:\temp\test_trace.xem'') ALTER EVENT SESSION [test_trace] ON SERVER STATE = START ' EXEC sp_executesql @Dynsql GO EXEC dbo.Proc1 GO ALTER EVENT SESSION [test_trace] ON SERVER STATE = STOP
可以使用读取跟踪数据
SELECT CONVERT (XML,event_data) AS data FROM sys.fn_xe_file_target_read_file ('C:\Temp\test_trace*.xel','C:\Temp\test_trace*.xem',NULL,NULL)
调用堆栈部分是
<action name="tsql_stack" package="sqlserver"> <value><frame level='1' handle='0x03000800E8EA0D0612E4EB00A59D00000000000000000000' line='6' offsetStart='228' offsetEnd='264'/> <frame level='2' handle='0x03000800921155002C81E700A59D00000000000000000000' line='8' offsetStart='258' offsetEnd='398'/> <frame level='3' handle='0x03000800CB3549012F81E700A59D00000100000000000000' line='5' offsetStart='90' offsetEnd='284'/> <frame level='4' handle='0x03000800045A3D022F81E700A59D00000100000000000000' line='5' offsetStart='90' offsetEnd='120'/> <frame level='5' handle='0x030008003D7E31033081E700A59D00000100000000000000' line='5' offsetStart='90' offsetEnd='120'/> <frame level='6' handle='0x0300080076A225043081E700A59D00000100000000000000' line='5' offsetStart='90' offsetEnd='120'/> <frame level='7' handle='0x010008002E775203603D9A0D000000000000000000000000' line='2' offsetStart='4' offsetEnd='-1'/></value> <text /> </action>
加入DMV
WITH CapturedResults AS ( SELECT data.value ( '(/event/@timestamp)[1]','DATETIME') AS [TIME],data.value ( '(/event/data[@name=''cpu'']/value)[1]','INT') AS [cpu (ms)],CONVERT (FLOAT,data.value ('(/event/data[@name=''duration'']/value)[1]','BIGINT')) / 1000000 AS [Duration (s)],data.value ( '(/event/action[@name=''sql_text'']/value)[1]','VARCHAR(MAX)') AS [sql STATEMENT],CAST(data.value('(/event/action[@name="tsql_stack"]/value)[1]','varchar(MAX)') AS XML) AS [stack_xml] FROM (SELECT CONVERT (XML,event_data) AS data FROM sys.fn_xe_file_target_read_file ('C:\Temp\test_trace*.xel',NULL) ) entries ),StackData AS ( SELECT frame_xml.value('(./@level)','int') AS [frame_level],frame_xml.value('(./@handle)','varchar(MAX)') AS [sql_handle],frame_xml.value('(./@offsetStart)','int') AS [offset_start],frame_xml.value('(./@offsetEnd)','int') AS [offset_end] FROM CapturedResults CROSS APPLY stack_xml.nodes('//frame') N (frame_xml) ) SELECT sd.frame_level,object_name(st.objectid,st.dbid) AS ObjectName,SUBSTRING(st.text,(sd.offset_start/2)+1,(( CASE sd.offset_end WHEN -1 THEN DATALENGTH(st.text) ELSE sd.offset_end END - sd.offset_start)/2) + 1) AS statement_text,qp.query_plan,qs2.creation_time,qs2.last_execution_time,qs2.execution_count FROM StackData AS sd CROSS APPLY sys.dm_exec_sql_text(CONVERT(VARBINARY(MAX),sd.sql_handle,1)) AS st LEFT OUTER JOIN sys.dm_exec_query_stats qs2 ON qs2.sql_handle = CONVERT(VARBINARY(MAX),1) OUTER APPLY sys.dm_exec_query_plan(CONVERT(VARBINARY(MAX),qs2.plan_handle,1)) AS qp