SQLサーバーに何がキャッシュされているかを示す次のクエリがあります。
SELECT cp.objtype AS ObjectType,
OBJECT_NAME(st.objectid,st.dbid) AS ObjectName,
cp.usecounts AS ExecutionCount,
st.TEXT AS QueryText,
qp.query_plan AS QueryPlan
FROM sys.dm_exec_cached_plans AS cp
CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) AS qp
CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) AS st
WHERE 1=1
--AND OBJECT_NAME(st.objectid,st.dbid) = 'YourObjectName'
AND query_plan IS NOT NULL
ORDER BY ExecutionCount DESC
欠落しているインデックスを探すためにqueryPlanフィールドをクエリできる方法はありますか?
以下のスクリプトを実行して、キャッシュされた実行プランで欠落しているインデックスを見つけることができます here
SELECT qp.query_plan
, total_worker_time/execution_count AS AvgCPU
, total_elapsed_time/execution_count AS AvgDuration
, (total_logical_reads+total_physical_reads)/execution_count AS AvgReads
, execution_count
, SUBSTRING(st.TEXT, (qs.statement_start_offset/2)+1 , ((CASE qs.statement_end_offset WHEN -1 THEN datalength(st.TEXT) ELSE qs.statement_end_offset END - qs.statement_start_offset)/2) + 1) AS txt
, qp.query_plan.value('declare default element namespace "http://schemas.Microsoft.com/sqlserver/2004/07/showplan"; (/ShowPlanXML/BatchSequence/Batch/Statements/StmtSimple/QueryPlan/MissingIndexes/MissingIndexGroup/@Impact)[1]' , 'decimal(18,4)') * execution_count AS TotalImpact
, qp.query_plan.value('declare default element namespace "http://schemas.Microsoft.com/sqlserver/2004/07/showplan"; (/ShowPlanXML/BatchSequence/Batch/Statements/StmtSimple/QueryPlan/MissingIndexes/MissingIndexGroup/MissingIndex/@Database)[1]' , 'varchar(100)') AS [DATABASE]
, qp.query_plan.value('declare default element namespace "http://schemas.Microsoft.com/sqlserver/2004/07/showplan"; (/ShowPlanXML/BatchSequence/Batch/Statements/StmtSimple/QueryPlan/MissingIndexes/MissingIndexGroup/MissingIndex/@Table)[1]' , 'varchar(100)') AS [TABLE]
FROM sys.dm_exec_query_stats qs
cross apply sys.dm_exec_sql_text(sql_handle) st
cross apply sys.dm_exec_query_plan(plan_handle) qp
WHERE qp.query_plan.exist('declare default element namespace "http://schemas.Microsoft.com/sqlserver/2004/07/showplan";/ShowPlanXML/BatchSequence/Batch/Statements/StmtSimple/QueryPlan/MissingIndexes/MissingIndexGroup/MissingIndex[@Database!="m"]') = 1
ORDER BY TotalImpact DESC
それはSQL Server 2008と2012で私にとってはうまくいきましたが、それでもSQL Server 2014で見ているように機能するかどうかを確認する必要があります。
また、
上記に加えて、次を読むことをお勧めします プランキャッシュ内のクエリが特定のインデックスを使用していることを確認する 、これは、現在の実行に使用されているこれらのインデックスの使用法を理解するのに非常に役立つジョナサンのすばらしいスクリプトですキャッシュ内の計画。
(バッチ全体ではなく)クエリで不足しているインデックスをバッチで検索する場合は、_sys.dm_exec_query_plan
_ではなく sys.dm_exec_text_query_plan()
を使用します。これは、バッチ全体(ストアドプロシージャや関数など)ではなく、_statement_start_offset
_および_statement_end_offset
_を使用して、実際のクエリのプランを返します。
_CREATE TABLE #query_cache
(
PlanHandle VARBINARY(64),
DatabaseName VARCHAR(255),
SchemaName VARCHAR(50),
ObjectName VARCHAR(50),
ExecutionCount BIGINT,
StatementText NVARCHAR(MAX),
StatementStart BIGINT,
StatementEnd BIGINT,
QueryPlan XML
);
_
まず、_plan_handle
_、_statement_start_offset
_、statement_end_offset`とともに、クエリのユニバースをキャッシュに構築します。これは、最後の日に実行されたすべてのものを取ります。
_SELECT
deqs.plan_handle AS PlanHandle
,DB_NAME(CAST(depa.value AS SMALLINT)) AS DatabaseName
,OBJECT_SCHEMA_NAME(dest.objectid, CAST(depa.value AS INT)) AS SchemaName
,OBJECT_NAME(dest.objectid, CAST(depa.value AS INT)) AS ObjectName
,SUM(deqs.execution_count) AS ExecutionCount
,Q.statementtext
,MAX(statement_start_offset) statement_start_offset
,MAX(statement_end_offset) statement_end_offset
INTO #query_cache
FROM sys.dm_exec_query_stats AS deqs
CROSS APPLY sys.dm_exec_sql_text(deqs.sql_handle) AS dest
CROSS APPLY sys.dm_exec_plan_attributes(plan_handle) AS depa
CROSS APPLY (VALUES (SUBSTRING(dest.text, (deqs.statement_start_offset/2)+1,
((CASE deqs.statement_end_offset WHEN -1 THEN DATALENGTH(dest.text)
ELSE deqs.statement_end_offset
END - deqs.statement_start_offset)/2)+1))) AS Q(statementtext)
WHERE deqs.last_execution_time > DATEADD(DAY, -1, GETDATE())
AND depa.attribute = 'dbid'
GROUP BY
dest.text, Q.statementtext, deqs.plan_handle, dest.objectid, depa.value;
_
残念ながら、_dm_exec_text_query_plan
_はNVARCHAR(MAX)
としてプランを返すため、ここにTRY_CAST()
をXMLで実行するための中間ステップがあります(2012以降の場合)。どうやらNVARCHAR
がXML
にキャストされない場合があるため、本番環境でこれを自動化している場合は、何かを壊さないようにすることをお勧めします。
このビットは少し遅いです...
_UPDATE qc
SET QueryPlan = TRY_CAST(detqp.query_plan AS XML)
FROM #query_cache AS qc
CROSS APPLY sys.dm_exec_text_query_plan(PlanHandle, qc.StatementStart, qc.StatementEnd) AS detqp
WHERE qc.StatementText IS NOT NULL;
_
次に、キャッシュを使用して、警告の変換、インデックスの欠落、キーの検索など、さまざまなメトリックを取得できます。
_WITH
XMLNAMESPACES (DEFAULT N'http://schemas.Microsoft.com/sqlserver/2004/07/showplan')
SELECT
DatabaseName
,SchemaName
,ObjectName
,ExecutionCount
,StatementText
,qc.QueryPlan.value('(/ShowPlanXML/BatchSequence/Batch/Statements/*/@StatementType)[1]', 'varchar(50)') StatementType
,qc.QueryPlan.value('(/ShowPlanXML/BatchSequence/Batch/Statements/*/@StatementOptmEarlyAbortReason)[1]', 'varchar(50)') StatementOptmEarlyAbortReason
,qc.QueryPlan.value('count(/ShowPlanXML/BatchSequence/Batch/Statements/*/QueryPlan/Warnings/PlanAffectingConvert)', 'int') ConvertWarnings
,qc.QueryPlan.value('count(/ShowPlanXML/BatchSequence/Batch/Statements/*/QueryPlan/Warnings/NoJoinPredicate)', 'int') NoJoinPredicateWarnings
,qc.QueryPlan.value('count(/ShowPlanXML/BatchSequence/Batch/Statements/*/QueryPlan/MissingIndexes/MissingIndexGroup/MissingIndex)', 'int') MissingIndexes
,qc.QueryPlan.value('count(.//RelOp[IndexScan[@Lookup="1"] and IndexScan/Object[@Schema!="[sys]"]])', 'int') KeyLookups
FROM #query_cache AS qc;
_
これの可能な改善:
master
やmsdb
などの特定のデータベースを除外します。
収集プロセスを自動化して定期的に収集する
IO/CPU /期間によって上位xクエリのみを返すようにフィルタリングします
最近、同様のニーズがありました。
answer by KASQLDBA は、最初に見つからないインデックスを計画から引き出します。複数存在する可能性があります。
そのため、次のコードを使用しました(ストアドプロシージャレベルの情報が必要でした-sys.dm_exec_query_stats
の代わりに sys.dm_exec_procedure_stats
これが望ましくない場合)
WITH XMLNAMESPACES
(DEFAULT 'http://schemas.Microsoft.com/sqlserver/2004/07/showplan')
SELECT [Procedure] = quotename(object_schema_name(object_id)) + '.' + quotename(object_name(object_id)),
Impact = MissingIndexGroup.n.value('@Impact', 'float'),
[Database] = MissingIndex.n.value('@Database', 'nvarchar(130)'),
[Schema] = MissingIndex.n.value('@Schema', 'nvarchar(130)'),
[Table] = MissingIndex.n.value('@Table', 'nvarchar(130)'),
EqualityColumns = SUBSTRING(eqColumns.list,2,8000),
InEqualityColumns = SUBSTRING(ineqColumns.list,2,8000),
IncludedColumns = SUBSTRING(incColumns.list,2,8000),
qp.query_plan
FROM sys.dm_exec_procedure_stats ps
cross apply sys.dm_exec_query_plan(ps.plan_handle) qp
cross apply qp.query_plan.nodes('//MissingIndexes/MissingIndexGroup') MissingIndexGroup(n)
cross apply MissingIndexGroup.n.nodes('MissingIndex') MissingIndex(n)
cross apply (SELECT ',' + n.n.value('@Name','nvarchar(130)') FROM MissingIndex.n.nodes('./ColumnGroup[@Usage="EQUALITY"]/Column') n(n) FOR XML PATH('')) eqColumns(list)
cross apply (SELECT ',' + n.n.value('@Name','nvarchar(130)') FROM MissingIndex.n.nodes('./ColumnGroup[@Usage="INEQUALITY"]/Column') n(n) FOR XML PATH('')) ineqColumns(list)
cross apply (SELECT ',' + n.n.value('@Name','nvarchar(130)') FROM MissingIndex.n.nodes('./ColumnGroup[@Usage="INCLUDE"]/Column') n(n) FOR XML PATH('')) incColumns(list)
where ps.database_id = db_id()