web-dev-qa-db-ja.com

dm_exec_query_statsを実行するパフォーマンスの低下

5分ごとに実行されるジョブで、通常1分未満で実行される、2年以上変更されていないDMVから情報を収集します。

先週の突然の開始のすべて、変更がデプロイされていない状態で、このジョブは15のうち1つのサーバーで12分以上実行を開始しました。実行プランを見ると、何も変わっていませんが、読み取り、書き込み、CPU時間、および行数はすべて行きました桁違いに。挿入される行の数もほとんど変わりません。

これは、基になるシステムテーブルの悪い統計ですか?もしそうなら、それはどのように解決できますか?存在しないオブジェクトについてのメッセージを与えるだけのDMVでUPDATE STATISTICSを実行できないからです。

問題のコードは次のとおりです。

SELECT
    ISNULL(DB_NAME(st.dbid), 'NULL') AS [DatabaseName]  
    ,ISNULL(OBJECT_SCHEMA_NAME(st.objectid, st.dbid), 'NULL') AS [SchemaName] 
    ,ISNULL(OBJECT_NAME(st.objectid, st.dbid), 'NULL') AS [ObjectName]   
    ,cp.objtype AS [ObjectType]
    ,qs.statement_start_offset AS [StatementStartOffset]
    ,qs.statement_end_offset AS [StatementEndOffset]
    ,qs.query_hash AS [QueryHash]
    ,qs.query_plan_hash AS [QueryPlanHash]
    ,qs.sql_handle AS [SQLHandle]
    ,qs.plan_handle AS [PlanHandle]
    ,qs.plan_generation_num AS [PlanGenerationNumber]
    ,cp.usecounts AS [UseCounts]
    ,cp.refcounts AS [RefCounts]
    -- find the offset of the actual statement being executed
    ,SUBSTRING(st.text, 
            CASE
                WHEN qs.statement_start_offset = 0 OR qs.statement_start_offset IS NULL THEN 1  
                ELSE qs.statement_start_offset/2 + 1
            END, 
            CASE 
                WHEN qs.statement_end_offset = 0 OR qs.statement_end_offset = -1 OR qs.statement_end_offset IS NULL THEN LEN(st.text)  
                ELSE qs.statement_end_offset/2 
            END - 
            CASE
                WHEN qs.statement_start_offset = 0 OR qs.statement_start_offset IS NULL THEN 1  
                ELSE qs.statement_start_offset/2
            END + 1 
        ) AS [Statement]  
    ,qs.last_execution_time AS [LastExecutionTime]
    ,qs.creation_time AS [CreationTime]
    ,qs.execution_count AS [ExecutionCount]
    ,qs.total_logical_reads AS [TotalLogicalReads]
    ,qs.last_logical_reads AS [LastLogicalReads]
    ,qs.min_logical_reads AS [MinLogicalReads]
    ,qs.max_logical_reads AS [MaxLogicalReads]
    ,qs.total_logical_writes AS [TotalLogicalWrites]
    ,qs.last_logical_writes AS [LastLogicalWrites]
    ,qs.min_logical_writes AS [MinLogicalWrites]
    ,qs.max_logical_writes AS [MaxLogicalWrites]
    ,qs.total_physical_reads AS [TotalPhysicalReads]
    ,qs.last_physical_reads AS [LastPhysicalReads]
    ,qs.min_physical_reads AS [MinPhysicalReads]
    ,qs.max_physical_reads AS [MaxPhysicalReads]
    ,qs.total_worker_time AS [TotalWorkerTime]
    ,qs.last_worker_time AS [LastWorkerTime]
    ,qs.min_worker_time AS [MinWorkerTime]
    ,qs.max_worker_time AS [MaxWorkerTime]
    ,qs.total_clr_time AS [TotalCLRTime]
    ,qs.last_clr_time AS [LastCLRTime]
    ,qs.min_clr_time AS [MinCLRTime]
    ,qs.max_clr_time AS [MaxCLRTime]
    ,qs.total_elapsed_time AS [TotalElapsedTime]
    ,qs.last_elapsed_time AS [LastElapsedTime]
    ,qs.min_elapsed_time AS [MinElapsedTime]
    ,qs.max_elapsed_time AS [MaxElapsedTime]
    ,qs.total_rows AS [TotalRows]
    ,qs.last_rows AS [LastRows]
    ,qs.min_rows AS [MinRows]
    ,qs.max_rows AS [MaxRows]
INTO #QueryUsageStats
FROM sys.dm_exec_query_stats qs   
INNER JOIN sys.dm_exec_cached_plans cp 
ON qs.plan_handle = cp.plan_handle 
CROSS APPLY sys.dm_exec_sql_text(qs.plan_handle) st
WHERE 1=2;

CREATE UNIQUE CLUSTERED INDEX #PK_#QueryUsageStats ON #QueryUsageStats
(
    [DatabaseName] ASC,
    [SchemaName] ASC,
    [ObjectName] ASC,
    [ObjectType] ASC,
    [StatementStartOffset] ASC,
    [StatementEndOffset] ASC,
    [QueryHash] ASC,
    [QueryPlanHash] ASC,
    [PlanHandle] ASC
);

INSERT INTO #QueryUsageStats
SELECT
    ISNULL(DB_NAME(st.dbid), 'NULL') AS [DatabaseName]  
    ,ISNULL(OBJECT_SCHEMA_NAME(st.objectid, st.dbid), 'NULL') AS [SchemaName] 
    ,ISNULL(OBJECT_NAME(st.objectid, st.dbid), 'NULL') AS [ObjectName]   
    ,cp.objtype AS [ObjectType]
    ,qs.statement_start_offset AS [StatementStartOffset]
    ,qs.statement_end_offset AS [StatementEndOffset]
    ,qs.query_hash AS [QueryHash]
    ,qs.query_plan_hash AS [QueryPlanHash]
    ,qs.sql_handle AS [SQLHandle]
    ,qs.plan_handle AS [PlanHandle]
    ,qs.plan_generation_num AS [PlanGenerationNumber]
    ,cp.usecounts AS [UseCounts]
    ,cp.refcounts AS [RefCounts]
    -- find the offset of the actual statement being executed
    ,SUBSTRING(st.text, 
            CASE
                WHEN qs.statement_start_offset = 0 OR qs.statement_start_offset IS NULL THEN 1  
                ELSE qs.statement_start_offset/2 + 1
            END, 
            CASE 
                WHEN qs.statement_end_offset = 0 OR qs.statement_end_offset = -1 OR qs.statement_end_offset IS NULL THEN LEN(st.text)  
                ELSE qs.statement_end_offset/2 
            END - 
            CASE
                WHEN qs.statement_start_offset = 0 OR qs.statement_start_offset IS NULL THEN 1  
                ELSE qs.statement_start_offset/2
            END + 1 
        ) AS [Statement]  
    ,qs.last_execution_time AS [LastExecutionTime]
    ,qs.creation_time AS [CreationTime]
    ,qs.execution_count AS [ExecutionCount]
    ,qs.total_logical_reads AS [TotalLogicalReads]
    ,qs.last_logical_reads AS [LastLogicalReads]
    ,qs.min_logical_reads AS [MinLogicalReads]
    ,qs.max_logical_reads AS [MaxLogicalReads]
    ,qs.total_logical_writes AS [TotalLogicalWrites]
    ,qs.last_logical_writes AS [LastLogicalWrites]
    ,qs.min_logical_writes AS [MinLogicalWrites]
    ,qs.max_logical_writes AS [MaxLogicalWrites]
    ,qs.total_physical_reads AS [TotalPhysicalReads]
    ,qs.last_physical_reads AS [LastPhysicalReads]
    ,qs.min_physical_reads AS [MinPhysicalReads]
    ,qs.max_physical_reads AS [MaxPhysicalReads]
    ,qs.total_worker_time AS [TotalWorkerTime]
    ,qs.last_worker_time AS [LastWorkerTime]
    ,qs.min_worker_time AS [MinWorkerTime]
    ,qs.max_worker_time AS [MaxWorkerTime]
    ,qs.total_clr_time AS [TotalCLRTime]
    ,qs.last_clr_time AS [LastCLRTime]
    ,qs.min_clr_time AS [MinCLRTime]
    ,qs.max_clr_time AS [MaxCLRTime]
    ,qs.total_elapsed_time AS [TotalElapsedTime]
    ,qs.last_elapsed_time AS [LastElapsedTime]
    ,qs.min_elapsed_time AS [MinElapsedTime]
    ,qs.max_elapsed_time AS [MaxElapsedTime]
    ,qs.total_rows AS [TotalRows]
    ,qs.last_rows AS [LastRows]
    ,qs.min_rows AS [MinRows]
    ,qs.max_rows AS [MaxRows]
FROM sys.dm_exec_query_stats qs   
INNER JOIN sys.dm_exec_cached_plans cp 
ON qs.plan_handle = cp.plan_handle 
CROSS APPLY sys.dm_exec_sql_text(qs.plan_handle) st;

作成された実行計画は次のとおりです。

enter image description here

また、クラスター化インデックスを削除し、クエリの結果を破棄する選択としてクエリを実行しましたが、それでも3分以上かかります。実際、sys.dm_exec_query_statsからデータを選択するだけでは、現在他のテーブルに結合していなくても、order by句がなくても3分以上かかるようです。

4
Diane

Nick CraverはOpserverでもこれでパフォーマンスの問題に遭遇しました。 これが彼の最終的なクエリです クエリプランでMSの担当者と直接作業した後-結合は少し歪んで見えますが、私が覚えているように、それは彼が一貫して優れたパフォーマンスを取得して回避できる唯一の方法でしたタイムアウトの問題:

SELECT AvgCPU, AvgDuration, AvgReads, AvgCPUPerMinute,
       TotalCPU, TotalDuration, TotalReads,
       PercentCPU, PercentDuration, PercentReads, PercentExecutions,
       ExecutionCount,
       ExecutionsPerMinute,
       PlanCreationTime, LastExecutionTime,
       SUBSTRING(st.text,
                 (StatementStartOffset / 2) + 1,
                 ((CASE StatementEndOffset
                   WHEN -1 THEN DATALENGTH(st.text)
                   ELSE StatementEndOffset
                   END - StatementStartOffset) / 2) + 1) AS QueryText,
        st.Text FullText,
        query_plan AS QueryPlan,
        PlanHandle,
        StatementStartOffset,
        StatementEndOffset,
        MinReturnedRows,
        MaxReturnedRows,
        AvgReturnedRows,
        TotalReturnedRows,
        LastReturnedRows,
        DB_NAME(DatabaseId) AS CompiledOnDatabase
FROM (SELECT TOP (@MaxResultCount) 
             total_worker_time / execution_count AS AvgCPU,
             total_elapsed_time / execution_count AS AvgDuration,
             total_logical_reads / execution_count AS AvgReads,
             Cast(total_worker_time / age_minutes As BigInt) AS AvgCPUPerMinute,
             execution_count / age_minutes AS ExecutionsPerMinute,
             Cast(total_worker_time / age_minutes_lifetime As BigInt) AS AvgCPUPerMinuteLifetime,
             execution_count / age_minutes_lifetime AS ExecutionsPerMinuteLifetime,
             total_worker_time AS TotalCPU,
             total_elapsed_time AS TotalDuration,
             total_logical_reads AS TotalReads,
             execution_count ExecutionCount,
             CAST(ROUND(100.00 * total_worker_time / t.TotalWorker, 2) AS MONEY) AS PercentCPU,
             CAST(ROUND(100.00 * total_elapsed_time / t.TotalElapsed, 2) AS MONEY) AS PercentDuration,
             CAST(ROUND(100.00 * total_logical_reads / t.TotalReads, 2) AS MONEY) AS PercentReads,
             CAST(ROUND(100.00 * execution_count / t.TotalExecs, 2) AS MONEY) AS PercentExecutions,
             qs.creation_time AS PlanCreationTime,
             qs.last_execution_time AS LastExecutionTime,
             qs.plan_handle AS PlanHandle,
             qs.statement_start_offset AS StatementStartOffset,
             qs.statement_end_offset AS StatementEndOffset,
             qs.min_rows AS MinReturnedRows,
             qs.max_rows AS MaxReturnedRows,
             CAST(qs.total_rows as MONEY) / execution_count AS AvgReturnedRows,
             qs.total_rows AS TotalReturnedRows,
             qs.last_rows AS LastReturnedRows,
             qs.sql_handle AS SqlHandle,
             Cast(pa.value as Int) DatabaseId
        FROM (SELECT *, 
                     CAST((CASE WHEN DATEDIFF(second, creation_time, GETDATE()) > 0 And execution_count > 1
                                THEN DATEDIFF(second, creation_time, GETDATE()) / 60.0
                                ELSE Null END) as MONEY) as age_minutes, 
                     CAST((CASE WHEN DATEDIFF(second, creation_time, last_execution_time) > 0 And execution_count > 1
                                THEN DATEDIFF(second, creation_time, last_execution_time) / 60.0
                                ELSE Null END) as MONEY) as age_minutes_lifetime
                FROM sys.dm_exec_query_stats) AS qs
             CROSS JOIN(SELECT SUM(execution_count) TotalExecs,
                               SUM(total_elapsed_time) TotalElapsed,
                               SUM(total_worker_time) TotalWorker,
                               SUM(total_logical_reads) TotalReads
                          FROM sys.dm_exec_query_stats) AS t
             CROSS APPLY sys.dm_exec_plan_attributes(qs.plan_handle) AS pa
     WHERE pa.attribute = 'dbid'
       {0}) sq
    CROSS APPLY sys.dm_exec_sql_text(SqlHandle) AS st
    CROSS APPLY sys.dm_exec_query_plan(PlanHandle) AS qp
3
Brent Ozar

これをさらに改善するには、last_execution_time> = @lastpolltimeでフィルタリングします。プランキャッシュに大量のチャーンがない限り、これにより、返される行数が削減されます。

1
Sqlgrease

基本的に各DMVを独自の一時テーブルに移植し、代わりにそれらの結合を行うことで、クエリを最適化することができました。また、最初にプランハンドルの個別のリストを取得し、それを使用してsys.dm_exec_sql_textにCROSS APPLYしました。これにより、読み取りがさらに削減されました。

これは次のようになります。

CREATE TABLE #ExecQueryStats (
    [StatementStartOffset] int NOT NULL
    ,[StatementEndOffset] int NOT NULL
    ,[QueryHash] binary(8) NULL
    ,[QueryPlanHash] binary(8) NULL
    ,[SQLHandle] varbinary(64) NOT NULL
    ,[PlanHandle] varbinary(64) NOT NULL
    ,[PlanGenerationNumber] int NULL
    ,[LastExecutionTime] datetime NULL
    ,[CreationTime] datetime NULL
    ,[ExecutionCount] bigint NOT NULL
    ,[TotalLogicalReads] bigint NOT NULL
    ,[LastLogicalReads] bigint NOT NULL
    ,[MinLogicalReads] bigint NOT NULL
    ,[MaxLogicalReads] bigint NOT NULL
    ,[TotalLogicalWrites] bigint NOT NULL
    ,[LastLogicalWrites] bigint NOT NULL
    ,[MinLogicalWrites] bigint NOT NULL
    ,[MaxLogicalWrites] bigint NOT NULL
    ,[TotalPhysicalReads] bigint NOT NULL
    ,[LastPhysicalReads] bigint NOT NULL
    ,[MinPhysicalReads] bigint NOT NULL
    ,[MaxPhysicalReads] bigint NOT NULL
    ,[TotalWorkerTime] bigint NOT NULL
    ,[LastWorkerTime] bigint NOT NULL
    ,[MinWorkerTime] bigint NOT NULL
    ,[MaxWorkerTime] bigint NOT NULL
    ,[TotalCLRTime] bigint NOT NULL
    ,[LastCLRTime] bigint NOT NULL
    ,[MinCLRTime] bigint NOT NULL
    ,[MaxCLRTime] bigint NOT NULL
    ,[TotalElapsedTime] bigint NOT NULL
    ,[LastElapsedTime] bigint NOT NULL
    ,[MinElapsedTime] bigint NOT NULL
    ,[MaxElapsedTime] bigint NOT NULL
    ,[TotalRows] bigint NULL
    ,[LastRows] bigint NULL
    ,[MinRows] bigint NULL
    ,[MaxRows] bigint NULL
);

CREATE CLUSTERED INDEX #PK_#ExecQueryStats ON #ExecQueryStats
(
    [PlanHandle] ASC
);

CREATE TABLE #ExecQueryStatsPlans (
    [PlanHandle] varbinary(64) NOT NULL
);

CREATE UNIQUE CLUSTERED INDEX #PK_#ExecQueryStatsPlans ON #ExecQueryStatsPlans
(
    [PlanHandle] ASC
);

CREATE TABLE #ExecCachedPlans (
    [PlanHandle] varbinary(64) NOT NULL
    ,[ObjectType] nvarchar(40) NOT NULL
    ,[UseCounts] int NOT NULL
    ,[RefCounts] int NOT NULL
    );

CREATE UNIQUE CLUSTERED INDEX #PK_#ExecCachedPlans ON #ExecCachedPlans
(
    [PlanHandle] ASC
);

CREATE TABLE #ExecSqlText (
    [PlanHandle] varbinary(64) NOT NULL
    ,[DatabaseName] sysname NULL
    ,[SchemaName] sysname NULL
    ,[ObjectName] sysname NULL 
    ,[Text] nvarchar(max) NULL
    );

CREATE UNIQUE CLUSTERED INDEX #PK_#ExecSqlText ON #ExecSqlText
(
    [PlanHandle] ASC
);

CREATE TABLE #QueryUsageStats (
    [DatabaseName] sysname NULL
    ,[SchemaName] sysname NULL
    ,[ObjectName] sysname NULL    
    ,[ObjectType] nvarchar(40) NOT NULL
    ,[StatementStartOffset] int NOT NULL
    ,[StatementEndOffset] int NOT NULL
    ,[QueryHash] binary(8) NULL
    ,[QueryPlanHash] binary(8) NULL
    ,[SQLHandle] varbinary(64) NOT NULL
    ,[PlanHandle] varbinary(64) NOT NULL
    ,[PlanGenerationNumber] int NULL
    ,[UseCounts] int NOT NULL
    ,[RefCounts] int NOT NULL
    ,[Statement] nvarchar(max) NULL 
    ,[LastExecutionTime] datetime NULL
    ,[CreationTime] datetime NULL
    ,[ExecutionCount] bigint NOT NULL
    ,[TotalLogicalReads] bigint NOT NULL
    ,[LastLogicalReads] bigint NOT NULL
    ,[MinLogicalReads] bigint NOT NULL
    ,[MaxLogicalReads] bigint NOT NULL
    ,[TotalLogicalWrites] bigint NOT NULL
    ,[LastLogicalWrites] bigint NOT NULL
    ,[MinLogicalWrites] bigint NOT NULL
    ,[MaxLogicalWrites] bigint NOT NULL
    ,[TotalPhysicalReads] bigint NOT NULL
    ,[LastPhysicalReads] bigint NOT NULL
    ,[MinPhysicalReads] bigint NOT NULL
    ,[MaxPhysicalReads] bigint NOT NULL
    ,[TotalWorkerTime] bigint NOT NULL
    ,[LastWorkerTime] bigint NOT NULL
    ,[MinWorkerTime] bigint NOT NULL
    ,[MaxWorkerTime] bigint NOT NULL
    ,[TotalCLRTime] bigint NOT NULL
    ,[LastCLRTime] bigint NOT NULL
    ,[MinCLRTime] bigint NOT NULL
    ,[MaxCLRTime] bigint NOT NULL
    ,[TotalElapsedTime] bigint NOT NULL
    ,[LastElapsedTime] bigint NOT NULL
    ,[MinElapsedTime] bigint NOT NULL
    ,[MaxElapsedTime] bigint NOT NULL
    ,[TotalRows] bigint NULL
    ,[LastRows] bigint NULL
    ,[MinRows] bigint NULL
    ,[MaxRows] bigint NULL
);

CREATE UNIQUE CLUSTERED INDEX #PK_#QueryUsageStats ON #QueryUsageStats
(
    [DatabaseName] ASC,
    [SchemaName] ASC,
    [ObjectName] ASC,
    [ObjectType] ASC,
    [StatementStartOffset] ASC,
    [StatementEndOffset] ASC,
    [QueryHash] ASC,
    [QueryPlanHash] ASC,
    [PlanHandle] ASC
);

INSERT INTO #ExecQueryStats
SELECT 
    qs.statement_start_offset AS [StatementStartOffset]
    ,qs.statement_end_offset AS [StatementEndOffset]
    ,qs.query_hash AS [QueryHash]
    ,qs.query_plan_hash AS [QueryPlanHash]
    ,qs.sql_handle AS [SQLHandle]
    ,qs.plan_handle AS [PlanHandle]
    ,qs.plan_generation_num AS [PlanGenerationNumber]
    ,qs.last_execution_time AS [LastExecutionTime]
    ,qs.creation_time AS [CreationTime]
    ,qs.execution_count AS [ExecutionCount]
    ,qs.total_logical_reads AS [TotalLogicalReads]
    ,qs.last_logical_reads AS [LastLogicalReads]
    ,qs.min_logical_reads AS [MinLogicalReads]
    ,qs.max_logical_reads AS [MaxLogicalReads]
    ,qs.total_logical_writes AS [TotalLogicalWrites]
    ,qs.last_logical_writes AS [LastLogicalWrites]
    ,qs.min_logical_writes AS [MinLogicalWrites]
    ,qs.max_logical_writes AS [MaxLogicalWrites]
    ,qs.total_physical_reads AS [TotalPhysicalReads]
    ,qs.last_physical_reads AS [LastPhysicalReads]
    ,qs.min_physical_reads AS [MinPhysicalReads]
    ,qs.max_physical_reads AS [MaxPhysicalReads]
    ,qs.total_worker_time AS [TotalWorkerTime]
    ,qs.last_worker_time AS [LastWorkerTime]
    ,qs.min_worker_time AS [MinWorkerTime]
    ,qs.max_worker_time AS [MaxWorkerTime]
    ,qs.total_clr_time AS [TotalCLRTime]
    ,qs.last_clr_time AS [LastCLRTime]
    ,qs.min_clr_time AS [MinCLRTime]
    ,qs.max_clr_time AS [MaxCLRTime]
    ,qs.total_elapsed_time AS [TotalElapsedTime]
    ,qs.last_elapsed_time AS [LastElapsedTime]
    ,qs.min_elapsed_time AS [MinElapsedTime]
    ,qs.max_elapsed_time AS [MaxElapsedTime]
    ,qs.total_rows AS [TotalRows]
    ,qs.last_rows AS [LastRows]
    ,qs.min_rows AS [MinRows]
    ,qs.max_rows AS [MaxRows]
FROM sys.dm_exec_query_stats qs;

INSERT INTO #ExecQueryStatsPlans
SELECT DISTINCT [PlanHandle]
FROM #ExecQueryStats;

INSERT INTO #ExecCachedPlans
SELECT 
    #ExecQueryStatsPlans.[PlanHandle]
    ,cp.objtype AS [ObjectType]
    ,cp.usecounts AS [UseCounts]
    ,cp.refcounts AS [RefCounts]
FROM sys.dm_exec_cached_plans cp
INNER JOIN #ExecQueryStatsPlans
ON cp.[plan_handle] = #ExecQueryStatsPlans.[PlanHandle];

INSERT INTO #ExecSqlText
SELECT
    #ExecQueryStatsPlans.[PlanHandle]
    ,ISNULL(DB_NAME(st.dbid), 'NULL') AS [DatabaseName]  
    ,ISNULL(OBJECT_SCHEMA_NAME(st.objectid, st.dbid), 'NULL') AS [SchemaName] 
    ,ISNULL(OBJECT_NAME(st.objectid, st.dbid), 'NULL') AS [ObjectName]   
    ,st.text AS [Text]
FROM #ExecQueryStatsPlans
CROSS APPLY sys.dm_exec_sql_text(#ExecQueryStatsPlans.[PlanHandle]) st;

INSERT INTO #QueryUsageStats
SELECT
    st.[DatabaseName]  
    ,st.[SchemaName] 
    ,st.[ObjectName]   
    ,cp.[ObjectType]
    ,qs.[StatementStartOffset]
    ,qs.[StatementEndOffset]
    ,qs.[QueryHash]
    ,qs.[QueryPlanHash]
    ,qs.[SQLHandle]
    ,qs.[PlanHandle]
    ,qs.[PlanGenerationNumber]
    ,cp.[UseCounts]
    ,cp.[RefCounts]
    -- find the offset of the actual statement being executed
    ,SUBSTRING(st.[Text], 
            CASE
                WHEN qs.[StatementStartOffset] = 0 OR qs.[StatementStartOffset] IS NULL THEN 1  
                ELSE qs.[StatementStartOffset]/2 + 1
            END, 
            CASE 
                WHEN qs.[StatementEndOffset] = 0 OR qs.[StatementEndOffset] = -1 OR qs.[StatementEndOffset] IS NULL THEN LEN(st.[Text])  
                ELSE qs.[StatementEndOffset]/2 
            END - 
            CASE
                WHEN qs.[StatementStartOffset] = 0 OR qs.[StatementStartOffset] IS NULL THEN 1  
                ELSE qs.[StatementStartOffset]/2
            END + 1 
        ) AS [Statement]  
    ,qs.[LastExecutionTime]
    ,qs.[CreationTime]
    ,qs.[ExecutionCount]
    ,qs.[TotalLogicalReads]
    ,qs.[LastLogicalReads]
    ,qs.[MinLogicalReads]
    ,qs.[MaxLogicalReads]
    ,qs.[TotalLogicalWrites]
    ,qs.[LastLogicalWrites]
    ,qs.[MinLogicalWrites]
    ,qs.[MaxLogicalWrites]
    ,qs.[TotalPhysicalReads]
    ,qs.[LastPhysicalReads]
    ,qs.[MinPhysicalReads]
    ,qs.[MaxPhysicalReads]
    ,qs.[TotalWorkerTime]
    ,qs.[LastWorkerTime]
    ,qs.[MinWorkerTime]
    ,qs.[MaxWorkerTime]
    ,qs.[TotalCLRTime]
    ,qs.[LastCLRTime]
    ,qs.[MinCLRTime]
    ,qs.[MaxCLRTime]
    ,qs.[TotalElapsedTime]
    ,qs.[LastElapsedTime]
    ,qs.[MinElapsedTime]
    ,qs.[MaxElapsedTime]
    ,qs.[TotalRows]
    ,qs.[LastRows]
    ,qs.[MinRows]
    ,qs.[MaxRows]
FROM #ExecQueryStats qs   
INNER JOIN #ExecCachedPlans cp 
ON qs.[PlanHandle] = cp.[PlanHandle] 
INNER JOIN #ExecSqlText st
ON qs.[PlanHandle] = st.[PlanHandle];

これにより、7〜8分から約2〜3分に短縮されました。 "SELECT INTO ... WHERE 1 = 2"メソッドを使用するのではなく、Aaronの提案に従って一時テーブルを手動で作成したことに注意してください。

キャッシュが突然膨らんでいないことを考えると、これが変更される原因を知りたいのですが。 1つのDBでクエリストアを有効にしましたが、一時的に無効にしてデータをパージした後でも、影響はありませんでした。サーバーで実行されているアドホックまたはコンパイル済みのクエリの数に大きな変化はないようです。また、1つのインスタンスでのみ発生します。私の直感は、これが統計の問題である可能性があるようにまだ感じていますが、これらのDMVのどこを探すべきかわかりません。プロシージャキャッシュをクリアすると役立つかどうかも疑問ですが、次のメンテナンスウィンドウまで待つ必要があります。

0
Diane