はじめに
SQL Server を運用していて、「急にシステムが遅くなった」「CPU 使用率が 100% に張り付いている」といったトラブルに遭遇することは珍しくありません。
原因調査において、以前は Windows のパフォーマンスモニター(Perfmon)と SQL Server の内部 ID を突き合わせる複雑な手順が使われていました。しかし、現在は SQL Server が標準で持っている 「動的管理ビュー(DMV: Dynamic Management Views)」 を活用するのが最も効率的で、推奨されるアプローチです。
DMV を使えば、OS にログインすることなく、SQL Server Management Studio(SSMS)からクエリを実行するだけで、「今まさに暴走しているクエリ」や「過去に最もリソースを消費した犯人」を即座に特定できます。
- リアルタイム調査: 現在実行中で、CPU を大量に消費しているクエリを特定する SQL
- 履歴調査: 過去の実行統計から、CPU 負荷が高いクエリのワーストランキングを出す SQL
- 検証: テスト用の負荷クエリを作成し、実際に特定できるか確認する手順
【リアルタイム】現在実行中の高負荷クエリを特定する
サーバーの CPU 使用率が急上昇している場合、何らかの重い処理が現在進行形で走っている可能性が高いです。 この犯人を特定するには、実行中のリクエスト情報を持つ動的管理ビュー sys.dm_exec_requests と、SQL 文の中身を持つ sys.dm_exec_sql_text を組み合わせて確認します。

実行中のリクエスト一覧を取得するSQL
以下のクエリを SQL Server Management Studio(SSMS)で実行してください。 現在実行されているクエリのうち、CPU 消費時間が長い順にトップ 20 を表示します。
SELECT TOP 20
r.session_id,
r.status,
r.start_time,
r.command,
r.cpu_time AS [CPU時間(ms)],
r.total_elapsed_time AS [経過時間(ms)],
t.text AS [実行中のSQL文]
FROM
sys.dm_exec_requests r
CROSS APPLY
sys.dm_exec_sql_text(r.sql_handle) t
WHERE
r.session_id > 50 -- システムプロセスを除外
AND r.session_id <> @@SPID -- 自分自身を除外
ORDER BY
r.cpu_time DESC;sys.dm_exec_requests と sys.dm_exec_sql_text の活用
このクエリの仕組みはシンプルです。
sys.dm_exec_requests-
現在 SQL Server 内で実行されているすべてのリクエスト(クエリ)のステータスや消費リソース情報をリアルタイムで保持しています。
sys.dm_exec_sql_text-
リクエストに含まれる「SQL ハンドル」という識別子を元に、実際の SQL 本文を復元する関数です。 これらを
CROSS APPLYで結合することで、「どのセッションが、どんな SQL を実行していて、どれくらい CPU を使っているか」が一目瞭然になります。
実行結果の見方(session_id, cpu_time, text)
結果が表示されたら、以下のカラムに注目してください。
cpu_time-
このリクエストが消費した CPU 時間(ミリ秒)です。ここが異常に高い値になっているものが、サーバーに負荷をかけている「犯人」です。
text-
実際に実行されている SQL 文です。これにより、どのアプリケーションのどの処理が原因かを特定できます。
session_id-
クエリを実行しているセッションの ID です。緊急停止させる場合(KILL コマンドなど)にこの ID を使用します。
status-
running(実行中)やrunnable(CPU 空き待ち)になっている場合、CPU リソースを激しく奪い合っている可能性があります。

このクエリを実行することで、パフォーマンスモニターやプロファイラを使わずとも、「今まさに暴走しているクエリ」 を特定することができます。
【履歴】過去にCPUを多く消費した「重いクエリ」トップ10を特定する
現在実行中のクエリに怪しいものがない場合、すでに処理が完了したクエリや、頻繁に実行される短いクエリが原因である可能性があります。 その場合は、SQL Server がメモリ内に保持している「実行統計情報のキャッシュ」を調査します。


蓄積された統計情報からワーストランキングを出す SQL
以下のクエリを実行すると、サーバー起動(またはキャッシュクリア)以降に実行されたクエリの中で、「CPU を最も多く消費したクエリ」のトップ 10 を抽出できます。
SELECT TOP 10
qs.total_worker_time / 1000 AS [CPU総使用時間(ms)],
qs.execution_count AS [実行回数],
(qs.total_worker_time / qs.execution_count) / 1000 AS [平均CPU時間(ms)],
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) AS [クエリテキスト]
FROM
sys.dm_exec_query_stats AS qs
CROSS APPLY
sys.dm_exec_sql_text(qs.sql_handle) AS qt
ORDER BY
qs.total_worker_time DESC;sys.dm_exec_query_stats の活用
この動的管理ビュー sys.dm_exec_query_stats は、クエリプランがキャッシュされている間の累積統計情報を保持しています。 これと sys.dm_exec_sql_text を組み合わせることで、「どのクエリが」「合計でどれくらい CPU を使い」「何回実行されたか」 を正確に把握できます。
なぜ「累積値」を見る必要があるのか
パフォーマンスチューニングにおいて、1回だけ実行される「重いクエリ」よりも、「軽量だが膨大な回数実行されるクエリ」 の方がシステム全体への負荷が高いケースが多々あります。
- 単発の重いクエリ: 瞬発的なスパイクの原因(
平均CPU時間で判別可能) - 頻出する軽いクエリ: 慢性的な高負荷の原因(
CPU総使用時間で判別可能)
上記の SQL は total_worker_time(CPU 総使用時間)の降順でソートしているため、「サーバーのリソースを最も食いつぶしている真犯人」 を効率的に特定できます。チューニングの効果が最も高いのは、このランキング上位のクエリです。
テスト用の CPU 負荷を作成して検証する
実際に高負荷な状態を作り出し、先ほど紹介した DMV で正しく特定できるかを確認してみましょう。
無限ループによる負荷生成クエリ(WHILE 文)
SQL Server Management Studio(SSMS)で「新しいクエリ」を開き、以下の SQL を実行します。 このクエリは WHILE (1=1) により無限ループし、ひたすら変数の加算を行うことで CPU リソースを消費し続けます。
DECLARE @i bigint = 0;
-- 無限ループでCPU負荷をかける
WHILE (1=1)
BEGIN
SET @i = @i + 1;
END


実行すると「クエリを実行中…」のまま終わりません。検証が終わったら必ず赤い「停止」ボタン(または Alt + Break)で止めてください。
実際に手順1、2で特定できるか確認
上記の負荷クエリを実行したまま、別のクエリウィンドウ を開き、先ほどの「手順1(リアルタイム調査用 SQL)」を実行してください。
結果のトップに、この WHILE ループが表示されているはずです。
text:WHILE (1=1) BEGIN SET @i = @i + 1; ENDが表示されるcpu_time: 実行している間、どんどん数値が増えていく
これにより、「サーバーが重い!」と感じた時に、DMV を使えば即座に原因のクエリを特定できることが確認できました。
まとめ
本記事では、SQL Server で CPU 負荷が高いクエリを特定する方法について解説しました。
- 現代の標準は DMV
-
SQL Server 標準の動的管理ビューを使えば、SSMS だけで完結し、情報の精度も高いです。
- 現在進行形なら:
sys.dm_exec_requestsで犯人を現行犯逮捕 - 過去の履歴なら:
sys.dm_exec_query_statsでワーストランキングを作成
- 現在進行形なら:
以上、最後までお読みいただきありがとうございました。


