获取负责查询的用户名和/或 IP 地址

Hau*_*uri 6 performance sql-server users query-performance performance-tuning

我使用以下查询来查找查询中的性能改进:

SELECT TOP 20 SUBSTRING(qt.text, (qs.statement_start_offset/2)+1, 
        ((CASE qs.statement_end_offset
          WHEN -1 THEN DATALENGTH(qt.text)
         ELSE qs.statement_end_offset
         END - qs.statement_start_offset)/2)+1),
  qs.execution_count,
  qs.total_logical_reads, qs.last_logical_reads, qs.min_logical_reads,
  qs.max_logical_reads, qs.total_physical_reads, qs.last_physical_reads,
  qs.min_physical_reads, qs.max_physical_reads,
  qs.total_elapsed_time / 1000000 As total_elapsed_time, 
  qs.last_elapsed_time / 1000000 As last_elapsed_time,
  qs.min_elapsed_time / 1000000 As min_elapsed_time, 
  qs.max_elapsed_time / 1000000 As max_elapsed_time,
  qs.last_execution_time, qs.creation_time, qp.query_plan
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) qt
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) qp
WHERE qt.encrypted=0
AND last_execution_time >= DATEADD (MINUTE , -5 , CURRENT_TIMESTAMP )
ORDER BY qs.total_logical_reads DESC
--ORDER BY qs.total_physical_reads DESC
Run Code Online (Sandbox Code Playgroud)

问题是我发现一个查询似乎不时重复抛出并需要增强,但我无法确定它来自哪里(可能是用户或程序,但无论如何应该有登录名或用户名)。如何获取此用户或跟踪此查询,以便对其进行改进?

Tho*_*ger 10

您可以使用以下查询来拉取当前正在执行的请求和相应的会话/连接信息:

select
    r.session_id,
    s.login_name,
    c.client_net_address,
    s.host_name,
    s.program_name,
    st.text
from sys.dm_exec_requests r
inner join sys.dm_exec_sessions s
on r.session_id = s.session_id
left join sys.dm_exec_connections c
on r.session_id = c.session_id
outer apply sys.dm_exec_sql_text(r.sql_handle) st
--where st.text like '%your query string to search for%';
Run Code Online (Sandbox Code Playgroud)

您在问题中查询的sys.dm_exec_query_stats是不按会话/连接的累积和汇总统计信息。换句话说,该信息并未存储在那里,也无法轻松检索。

您可以放置​​一个 SQL 跟踪或 XE 会话来获取您正在寻找的数据,基本上是监控您的违规查询的发生。

有几种方法可以获取您正在寻找的数据,但不幸的是,对于您给定的查询,没有一种简单的方法可以将其转化为您正在寻找的数据。只是不同类型的数据(您提供聚合数据,但您正在寻找快照和粒度数据)。


Ken*_*her 6

我会说你最好的选择是扩展事件或跟踪。不幸的是,我对扩展事件知之甚少,无法为您提供任何建议,但我可以为您提供一些有关设置跟踪的信息。

首先,跟踪可以拉取主机名,这是查询来自的服务器/工作站。包括它,因为有时用户名并没有真正的帮助,您可以经常通过他们运行的机器来追踪谁在做某事。

其次,有一个“textdata”过滤器,您应该只能提取那些以您要查找的查询的第一个字符开头的查询。但是,您可能会发现这有点慢,而且我不确定回车/换行符或其他格式是否会使其失效。您可以进行跟踪,其中仅提取具有 x 持续时间或更长、x 读取或更高、x 写入或更高等的那些查询。通过使用上述查询中的信息,您应该能够保持“误报”响应的数量相当低。使用这些类型的限制应该会使您的跟踪开销相当低。

编辑:这是创建您可以使用的跟踪的脚本示例。我已经评论了我觉得你需要的一切。与运行 GUI 相比,为文件编写脚本使用的开销要少得多。您只需要注意处理定期创建的文件并删除它们。完成跟踪后,请确保将其关闭。

我将在跟踪创建脚本下包含一个脚本,以帮助您在完成后将其关闭。

-- Create a Queue
declare @rc int
declare @TraceID int
declare @maxfilesize bigint
set @maxfilesize = 5

-- Note that this will start a new trace file each time the old ones get to 5mb.  
-- This way you can process and remove the older files.  Be careful to shut down your
-- trace when you are done as a runaway trace can use up all the space on your drive.
exec @rc = sp_trace_create @TraceID output, 0, N'C:\TraceFiles\QueryTrace', @maxfilesize, NULL
if (@rc != 0) goto error

-- Set the events
declare @on bit
set @on = 1
-- Here are the events the trace will look for along with what data
-- will be pulled for each event.  I've labeled the events by group
-- and for the first event I've labeled each of the data points.
-- Look up sp_trace_setevent in BOL for further descriptions.

-- Remote procedure call completed
exec sp_trace_setevent @TraceID, 10, 15, @on -- EndTime
exec sp_trace_setevent @TraceID, 10, 8, @on -- HostName
exec sp_trace_setevent @TraceID, 10, 16, @on -- Reads
exec sp_trace_setevent @TraceID, 10, 1, @on -- TextData
exec sp_trace_setevent @TraceID, 10, 9, @on -- ClientProcessId
exec sp_trace_setevent @TraceID, 10, 17, @on -- Writes
exec sp_trace_setevent @TraceID, 10, 10, @on -- ApplicationName
exec sp_trace_setevent @TraceID, 10, 18, @on -- CPU
exec sp_trace_setevent @TraceID, 10, 34, @on -- ObjectName
exec sp_trace_setevent @TraceID, 10, 3, @on -- DatabaseId
exec sp_trace_setevent @TraceID, 10, 11, @on -- LoginName
exec sp_trace_setevent @TraceID, 10, 12, @on -- SPID
exec sp_trace_setevent @TraceID, 10, 13, @on -- Duration
exec sp_trace_setevent @TraceID, 10, 6, @on -- NTUserName
exec sp_trace_setevent @TraceID, 10, 14, @on -- StartTime
-- SQL Batch completed
exec sp_trace_setevent @TraceID, 12, 15, @on
exec sp_trace_setevent @TraceID, 12, 8, @on
exec sp_trace_setevent @TraceID, 12, 16, @on
exec sp_trace_setevent @TraceID, 12, 1, @on
exec sp_trace_setevent @TraceID, 12, 9, @on
exec sp_trace_setevent @TraceID, 12, 17, @on
exec sp_trace_setevent @TraceID, 12, 6, @on
exec sp_trace_setevent @TraceID, 12, 10, @on
exec sp_trace_setevent @TraceID, 12, 14, @on
exec sp_trace_setevent @TraceID, 12, 18, @on
exec sp_trace_setevent @TraceID, 12, 3, @on
exec sp_trace_setevent @TraceID, 12, 11, @on
exec sp_trace_setevent @TraceID, 12, 12, @on
exec sp_trace_setevent @TraceID, 12, 13, @on
-- SQL Statement completed
exec sp_trace_setevent @TraceID, 41, 15, @on
exec sp_trace_setevent @TraceID, 41, 8, @on
exec sp_trace_setevent @TraceID, 41, 16, @on
exec sp_trace_setevent @TraceID, 41, 1, @on
exec sp_trace_setevent @TraceID, 41, 9, @on
exec sp_trace_setevent @TraceID, 41, 17, @on
exec sp_trace_setevent @TraceID, 41, 6, @on
exec sp_trace_setevent @TraceID, 41, 10, @on
exec sp_trace_setevent @TraceID, 41, 14, @on
exec sp_trace_setevent @TraceID, 41, 18, @on
exec sp_trace_setevent @TraceID, 41, 3, @on
exec sp_trace_setevent @TraceID, 41, 11, @on
exec sp_trace_setevent @TraceID, 41, 12, @on
exec sp_trace_setevent @TraceID, 41, 13, @on

-- Set the Filters
declare @intfilter int
declare @bigintfilter bigint

-- Here are the filters.  The more specific you can be the less overhead.
-- In your case you are working with a "problem" query so at least one of 
-- the following should be an issue:  duration, reads, writes.  With any
-- luck you don't have a lot of high resource queries on your instance
-- so these filters won't pull a tremendous amount more than what you 
-- are looking for.  

-- Obviously you will need to modify the filter values to fit your purpose.

-- Filter by database id
set @intfilter = 17
exec sp_trace_setfilter @TraceID, 3, 0, 0, @intfilter

-- Filter by duration. Greater than or equal to.
set @bigintfilter = 10000000
exec sp_trace_setfilter @TraceID, 13, 0, 4, @bigintfilter

-- Filter by reads. Greater than or equal to.
set @bigintfilter = 1000
exec sp_trace_setfilter @TraceID, 16, 0, 4, @bigintfilter

-- Filter by writes. Greater than or equal to.
set @bigintfilter = 1000
exec sp_trace_setfilter @TraceID, 17, 0, 4, @bigintfilter

-- This is generated by the GUI to filter out the profiler.
-- I'm striping off the GUID on the end as that would be 
-- specific to my machine (I believe).
-- exec sp_trace_setfilter @TraceID, 10, 0, 7, N'SQL Server Profiler - 99514c1d-7404-4448-8831-1225125c45aa'
exec sp_trace_setfilter @TraceID, 10, 0, 7, N'SQL Server Profiler'

-- Set the trace status to start
exec sp_trace_setstatus @TraceID, 1

-- display trace id for future references
select TraceID=@TraceID
goto finish

error: 
select ErrorCode=@rc

finish: 
go
Run Code Online (Sandbox Code Playgroud)

完成后关闭跟踪的脚本。

-- This query pulls a list of all traces.
-- You will be able to tell which one is yours
-- by the filename you selected when you
-- created it.
SELECT * FROM sys.fn_trace_getinfo(NULL)
-- Get the trace id from the query above and use it instead of @TraceID.
exec sp_trace_setstatus @TraceID, 0
exec sp_trace_setstatus @TraceID, 2
Run Code Online (Sandbox Code Playgroud)