DataBase  /  SQL Server  /  レポート取得ガイド

PART 03 — Query Store による取得とセクション定義

Query Store は SQL Server 2016 以降で利用できる機能で、 クエリのテキスト・実行プラン・実行統計履歴をデータベース内に自動保存します。 Oracle AWR の SQL Statistics セクションに最も近い機能であり、 期間指定でのクエリ性能比較・プランのリグレッション検知・プラン固定化が可能です。

1. Query Store の概要とアーキテクチャ

Query Store はデータベースレベルで有効化し、以下の情報を自動収集します。

  • クエリテキスト(query_store_query_text)
  • コンパイル済み実行プラン(query_store_plan)
  • 実行統計(query_store_runtime_stats)
  • 統計集計期間(query_store_runtime_stats_interval)
  • 待機統計 SQL Server 2017+(query_store_wait_stats)
💡 Oracle AWR との違い
Oracle AWR はインスタンス全体のスナップショットを定期取得するのに対し、 Query Store はクエリ単位で実行統計を連続的に記録します。 AWR の「SQL ordered by Elapsed Time」相当の情報を任意の期間で抽出でき、 プランの変遷もビューで追跡できる点が AWR を超える強みです。
⚠️ バージョン要件
Query Store は SQL Server 2016 (13.0) 以降で利用可能です。 待機統計の記録は SQL Server 2017 (14.0) 以降が必要です。 Azure SQL Database では全バージョンで有効化されています。

2. 有効化と設定

Query Store の有効化

T-SQL
-- データベース単位で有効化
ALTER DATABASE [YourDatabase]
SET QUERY_STORE = ON (
    OPERATION_MODE         = READ_WRITE,  -- READ_WRITE: 収集有効 / READ_ONLY: 収集停止・参照のみ
    CLEANUP_POLICY         = (STALE_QUERY_THRESHOLD_DAYS = 30),  -- データ保持日数
    DATA_FLUSH_INTERVAL_SECONDS = 900,    -- メモリ→ストレージへのフラッシュ間隔(秒)
    INTERVAL_LENGTH_MINUTES = 60,         -- 統計集計インターバル(分)
    MAX_STORAGE_SIZE_MB    = 1000,        -- Query Store が使用する最大ストレージ (MB)
    QUERY_CAPTURE_MODE     = AUTO,        -- AUTO: 重要クエリのみ / ALL: 全クエリ
    SIZE_BASED_CLEANUP_MODE = AUTO,       -- ストレージ上限到達時の自動クリーンアップ
    MAX_PLANS_PER_QUERY    = 200,         -- クエリ1件に保持するプランの最大数
    WAIT_STATS_CAPTURE_MODE = ON          -- 待機統計収集 (SQL Server 2017+)
);
GO

-- 有効化の確認
SELECT name, is_query_store_on
FROM sys.databases
WHERE name = 'YourDatabase';

設定のチューニングポイント

設定項目推奨値(OLTP)推奨値(DWH)説明
INTERVAL_LENGTH_MINUTES 60 60 ~ 120 統計集計の粒度。細かいほど詳細だがストレージ消費増
MAX_STORAGE_SIZE_MB 500 ~ 2048 2048 ~ 10240 少なすぎると古いデータが早期削除される
QUERY_CAPTURE_MODE AUTO AUTO ALL は全クエリ記録のためストレージ消費が大きい
STALE_QUERY_THRESHOLD_DAYS 30 60 ~ 90 長期傾向分析が必要な場合は延ばす

3. 主要カタログビューとセクション定義

query_store_query
クエリのコンパイル情報。query_hash・オブジェクト ID・コンテキスト設定などを保持。クエリ識別の基点となるビュー。
query_store_query_text
クエリテキスト(SQL 文)を保持。query_text_id で query_store_query と結合する。
query_store_plan
実行プランの XML(query_plan 列)と強制フラグ(is_forced_plan)を保持。プランの変遷を追跡できる。
query_store_runtime_stats
インターバルごとの実行統計(CPU・経過時間・論理読み取りなど)。Oracle AWR の SQL Statistics 直接対応ビュー。
query_store_runtime_stats_interval
統計集計インターバルの開始・終了時刻。期間指定クエリで結合して時間帯を絞り込む。
query_store_wait_stats
クエリ単位の待機統計(SQL Server 2017+)。wait_category_desc・total_query_wait_time_ms などを保持。

query_store_runtime_stats の主要列定義

列名説明Oracle AWR 対応
execution_type_desc 実行種別(Regular, Aborted, Exception)
count_executions インターバル内の実行回数 Executions
avg_duration 平均経過時間(マイクロ秒) Avg Elapsed Time
total_duration インターバル内の合計経過時間(マイクロ秒) Total Elapsed Time
avg_cpu_time 平均 CPU 時間(マイクロ秒) Avg CPU Time
avg_logical_io_reads 平均論理読み取り数(8KB ページ単位) Avg Buffer Gets
avg_physical_io_reads 平均物理読み取り数 Avg Disk Reads
avg_rowcount 平均処理行数 Rows Processed
avg_memory_grant_kb 平均メモリ許可量(KB)
last_execution_time 最後の実行時刻

4. 期間指定 SQL 統計取得クエリ集

Top SQL by 合計経過時間(特定期間)

T-SQL — AWR の SQL ordered by Elapsed Time 相当
-- 特定期間のTop SQL by 合計経過時間
-- @start_time / @end_time で分析期間を指定
DECLARE @start_time DATETIME2 = '2026-05-29 09:00:00';
DECLARE @end_time   DATETIME2 = '2026-05-29 12:00:00';

SELECT TOP 20
    qt.query_sql_text,
    q.query_id,
    p.plan_id,
    rs.count_executions,
    rs.avg_duration        / 1000.0   AS avg_duration_ms,
    rs.total_duration      / 1000.0   AS total_duration_ms,
    rs.avg_cpu_time        / 1000.0   AS avg_cpu_ms,
    rs.avg_logical_io_reads            AS avg_logical_reads,
    rs.avg_physical_io_reads           AS avg_physical_reads,
    rs.avg_rowcount,
    rs.avg_memory_grant_kb
FROM sys.query_store_runtime_stats      rs
JOIN sys.query_store_runtime_stats_interval rsi
    ON rs.runtime_stats_interval_id = rsi.runtime_stats_interval_id
JOIN sys.query_store_plan               p
    ON rs.plan_id = p.plan_id
JOIN sys.query_store_query              q
    ON p.query_id = q.query_id
JOIN sys.query_store_query_text         qt
    ON q.query_text_id = qt.query_text_id
WHERE rsi.start_time >= @start_time
  AND rsi.end_time   <= @end_time
  AND rs.execution_type = 0            -- Regular execution のみ
ORDER BY rs.total_duration DESC;

プランのリグレッション検知(今日 vs 昨日)

T-SQL — AWR の SQL Plan Statistics 相当
-- プランのリグレッション検知:昨日と今日で同一クエリの性能を比較
WITH today AS (
    SELECT
        q.query_id,
        SUM(rs.count_executions)  AS exec_today,
        AVG(rs.avg_duration)      AS avg_dur_today
    FROM sys.query_store_runtime_stats rs
    JOIN sys.query_store_runtime_stats_interval rsi
        ON rs.runtime_stats_interval_id = rsi.runtime_stats_interval_id
    JOIN sys.query_store_plan p ON rs.plan_id = p.plan_id
    JOIN sys.query_store_query q ON p.query_id = q.query_id
    WHERE rsi.start_time >= DATEADD(HOUR, -24, GETUTCDATE())
    GROUP BY q.query_id
),
yesterday AS (
    SELECT
        q.query_id,
        SUM(rs.count_executions)  AS exec_yesterday,
        AVG(rs.avg_duration)      AS avg_dur_yesterday
    FROM sys.query_store_runtime_stats rs
    JOIN sys.query_store_runtime_stats_interval rsi
        ON rs.runtime_stats_interval_id = rsi.runtime_stats_interval_id
    JOIN sys.query_store_plan p ON rs.plan_id = p.plan_id
    JOIN sys.query_store_query q ON p.query_id = q.query_id
    WHERE rsi.start_time >= DATEADD(HOUR, -48, GETUTCDATE())
      AND rsi.start_time  < DATEADD(HOUR, -24, GETUTCDATE())
    GROUP BY q.query_id
)
SELECT
    t.query_id,
    qt.query_sql_text,
    y.avg_dur_yesterday / 1000.0   AS avg_dur_yesterday_ms,
    t.avg_dur_today     / 1000.0   AS avg_dur_today_ms,
    CAST(
        (t.avg_dur_today - y.avg_dur_yesterday) * 100.0
        / NULLIF(y.avg_dur_yesterday, 0) AS DECIMAL(6,1)
    )                              AS pct_change
FROM today t
JOIN yesterday y ON t.query_id = y.query_id
JOIN sys.query_store_query q ON t.query_id = q.query_id
JOIN sys.query_store_query_text qt ON q.query_text_id = qt.query_text_id
WHERE t.avg_dur_today > y.avg_dur_yesterday * 1.5   -- 50% 以上悪化したもの
ORDER BY pct_change DESC;

5. Query Store 待機統計(SQL Server 2017+)

query_store_wait_stats ビューにより、クエリ単位の待機統計が取得できます。 「このクエリは何の待機で時間を使っているか」が一目でわかります。

列名説明
wait_category_desc 待機カテゴリ(CPU, Lock, Latch, Network IO, User IO, Log IO など)
total_query_wait_time_ms 合計待機時間(ミリ秒)
avg_query_wait_time_ms 平均待機時間(ミリ秒)
T-SQL — クエリ単位の待機統計(SQL Server 2017+)
-- クエリごとの待機カテゴリ内訳
SELECT TOP 20
    qt.query_sql_text,
    ws.wait_category_desc,
    SUM(ws.total_query_wait_time_ms)   AS total_wait_ms,
    AVG(ws.avg_query_wait_time_ms)     AS avg_wait_ms
FROM sys.query_store_wait_stats ws
JOIN sys.query_store_plan   p  ON ws.plan_id  = p.plan_id
JOIN sys.query_store_query  q  ON p.query_id  = q.query_id
JOIN sys.query_store_query_text qt ON q.query_text_id = qt.query_text_id
JOIN sys.query_store_runtime_stats_interval rsi
    ON ws.runtime_stats_interval_id = rsi.runtime_stats_interval_id
WHERE rsi.start_time >= DATEADD(HOUR, -24, GETUTCDATE())
GROUP BY qt.query_sql_text, ws.wait_category_desc
ORDER BY total_wait_ms DESC;

6. SSMS の Query Store GUI 操作

SSMS のオブジェクトエクスプローラーで対象データベースを展開すると、 「クエリストア」フォルダが表示されます。以下のレポートが利用可能です。

レポート名内容Oracle AWR 対応
最もリソースを消費するクエリ 期間・指標(CPU/経過時間/論理読み取り/実行回数等)でフィルタした Top SQL。プランをグラフで並べて比較できる。 SQL Statistics
クエリウォーター フォール 時系列での実行統計の推移をバブルチャートで表示。性能劣化の時点を視覚的に特定できる。
プランの変更クエリ プランが変わったクエリを列挙。変更前後の実行時間を比較し、リグレッションを検知。
強制されたクエリ プランを強制固定しているクエリの一覧。
全クエリのクエリ Query Store に記録された全クエリの一覧。フリーテキスト検索も可能。

7. プラン固定化(Plan Forcing)

プランのリグレッションが確認された場合、既知の良好なプランを固定できます。 Oracle の SQL Plan Management(SPM)のベースライン固定に相当します。

T-SQL — プランの固定化と解除
-- プランを固定する(plan_id は query_store_plan ビューで確認)
EXEC sys.sp_query_store_force_plan
    @query_id = 42,
    @plan_id  = 101;

-- 固定を解除する
EXEC sys.sp_query_store_unforce_plan
    @query_id = 42,
    @plan_id  = 101;

-- 現在強制されているプランの確認
SELECT
    q.query_id, qt.query_sql_text,
    p.plan_id, p.is_forced_plan,
    p.force_failure_count,
    p.last_force_failure_reason_desc
FROM sys.query_store_plan p
JOIN sys.query_store_query q ON p.query_id = q.query_id
JOIN sys.query_store_query_text qt ON q.query_text_id = qt.query_text_id
WHERE p.is_forced_plan = 1;
Automatic Plan Correction(SQL Server 2017+)
ALTER DATABASE [YourDB] SET AUTOMATIC_TUNING (FORCE_LAST_GOOD_PLAN = ON) を設定すると、 SQL Server がプランのリグレッションを検知して自動的に最良プランを固定します。 Oracle の SQL Plan Management の自動発展に相当する機能です。