捕获所有使用提示的查询

mvi*_*lar 2 sql-server-2005 sql-server

如何捕获或列出自上次实例重启以来使用提示并已执行的所有查询?

我使用的是 SQL Server 2005 标准版。

Aar*_*and 7

在我的刺激下,@buckley 已经提供了一个查询,您可以从中获取信息。我要补充的是,您可以定期捕获此 DMV 的快照,以便即使它们自上次使用以来已被推出缓存,您也可以捕获它们。我不会在这里讨论如何维护 DMV 数据的存档;也许正在制作一个很好的博客文章。

获得数据后(或仅查看缓存的当前计划),您可以从以下返回的 XML 中获得一些常见提示sys.dm_exec_query_plan.query_plan

    <QueryPlan      NonParallelPlanReason="MaxDOPSetToOne"
    ---- this query used OPTION (MAXDOP 1) ^^^^^^^^^^^^^^
    ---- or perhaps was forced using server maxdop settings or resource governor

    <IndexScan Ordered="0" ForcedIndex="0" ForceScan="1" NoExpandHint="0">
    ---- this query used WITH (FORCESCAN) ------------^

    <IndexScan Ordered="0" ForcedIndex="0" ForceScan="0" NoExpandHint="1">
    ---- this query used WITH (NOEXPAND) ------------------------------^

    <IndexScan Ordered="1" ScanDirection="FORWARD" ForcedIndex="0" ForceSeek="1" ...>
    ---- this query used WITH (FORCESEEK) ------------------------------------^

    <IndexScan Ordered="1" ScanDirection="FORWARD" ForcedIndex="1" ForceSeek="1"
    ---- this query uses WITH (FORCESEEK(index(col))) ----------^-------------^

    <IndexScan      Ordered="0" ForcedIndex="1" ForceScan="0" NoExpandHint="0">
    ---- this query used WITH (INDEX = ...) -^
Run Code Online (Sandbox Code Playgroud)

我不是 XML 专家,所以我不知道如何告诉您动态提取该信息。(更多关于这个下面。)

我看不到任何方法来确定是否已设置隔离(在批处理级别或使用类似的东西WITH (NOLOCK)) - 您可能必须:

  • 通过轮询捕获那些正在执行的操作sys.dm_exec_requests(尽管transaction_isolation_level实际上只告诉您诸如 之类的事情SET TRANSACTION ISOLATION LEVEL,而不是表级提示,因为您只能应用于NOLOCK查询中的某些表),或
  • 依赖于对查询文本本身的解析sys.dm_exec_sql_text(请注意,如果NOLOCK被注释掉,或者您有一个类似 的表名,这可能会产生误报dbo.SubmitNoLocks)。

我也看不出像OPTION (FORCE ORDER)or 之类的查询提示的计划有什么不同OPTION (FAST n),因此同样地,这些提示可能还需要依赖解析查询文本,而不是依赖计划 XML。

编辑

由于您可以从备用 DMF 获得计划的非 XML 版本,因此sys.dm_exec_text_query_plan您不必成为 XML 专家来执行此解析。例如,下面将捕获缓存中的所有查询计划所用WITH (INDEX...)WITH FORCESEEK[(...)]WITH FORCESCANWITH (NOEXPAND)WITH (NOLOCK)

SELECT t.[text]--, qp.query_plan 
FROM sys.dm_exec_cached_plans AS p
CROSS APPLY sys.dm_exec_sql_text(p.plan_handle) AS t
CROSS APPLY sys.dm_exec_text_query_plan(p.plan_handle, 0, -1) AS qp
WHERE 
 t.[text] NOT LIKE '%dm_exec%' -- to keep this query out of result
 AND
 (
  qp.query_plan LIKE '%ForceSeek="1"%'
  OR qp.query_plan LIKE '%Forcescan="1"%'
  OR qp.query_plan LIKE '%NoExpandHint="1"%'
  OR qp.query_plan LIKE '%ForcedIndex="1"%'
  OR t.[text] LIKE '%NOLOCK%'
 )
 -- t.[dbid] = DB_ID() -- to limit results, but may be too exclusionary
;
Run Code Online (Sandbox Code Playgroud)

如前所述,这可能会对这样的查询产生误报:

SELECT * FROM dbo.table /* WITH (NOLOCK) */
SELECT * FROM dbo.RenoLocksmiths;
Run Code Online (Sandbox Code Playgroud)

但是除了解析查询文本之外NOLOCK,对于所有计划属性 - 因为 XML 被实体化并且引号之类的字符被转义 - 它不会为这样的查询返回误报:

SELECT * FROM dbo.table /* Forceseek="1" */
SELECT * FROM dbo.[WITH (FORCESEEK)];
Run Code Online (Sandbox Code Playgroud)


buc*_*ley 5

回想起来这是不可能的。

您需要使用SQL Profiler主动记录查询,您可以在其中导出和过滤带有提示的查询。或者使用服务器端跟踪进行长时间运行的跟踪。

您可以通过检查缓存的执行计划来接近。

这将显示大多数查询,但请记住,有些查询没有被缓存,如果有压力,sql server 可以将它们从缓存中删除。使用此查询列出它们:

SELECT [cp].[refcounts] 
, [cp].[usecounts] 
, [cp].[objtype] 
, [st].[dbid] 
, [st].[objectid] 
, [st].[text] 
, [qp].[query_plan] 
FROM sys.dm_exec_cached_plans cp 
CROSS APPLY sys.dm_exec_sql_text ( cp.plan_handle ) st 
CROSS APPLY sys.dm_exec_query_plan ( cp.plan_handle ) qp ;
Run Code Online (Sandbox Code Playgroud)

您可以将 where 子句与正则表达式(clr 函数)一起使用,以便将误报最小化到接近 0。