SQL Serverでテーブルごとの統計情報更新日時を確認するSQL
公開日 2026-08-13 · 更新日 2026-08-13 · 最終検証日 2026-08-13
結論
SQL Serverでは `sys.stats` と `STATS_DATE()` を使って、テーブルおよび統計ごとの最終更新日時を確認できます。SQL Server 2008 R2 SP2 / 2012 SP1 以降であれば `sys.dm_db_stats_properties` を併用し、最終更新以降に変更された行数(`modification_counter`)まで同時に取得できます。以下のSQLはデータも設定も変更しない参照専用クエリで、本番環境でもそのまま実行できます。
この文書の適用条件
| 対象製品 | SQL Server / Amazon RDS for SQL Server / Azure SQL Managed Instance |
|---|---|
| 確認バージョン | SQL Server 2008 以降(`sys.dm_db_stats_properties` は 2008 R2 SP2 / 2012 SP1 以降) |
| 適用環境 | オンプレミス、EC2、Amazon RDS、Azure |
| 必要権限 | 対象データベースへの接続権限と、対象オブジェクトのメタデータ可視性(`sys.stats` はメタデータ可視性ルールに従い、権限のないオブジェクトは行として返りません) |
| 実行影響 | 確認用SQLは参照のみ(データ・設定を変更しません)。対応手順として掲載している `UPDATE STATISTICS` は統計の再作成とプラン再コンパイルを伴います。 |
| 再起動 | 不要 |
| 最終検証日 | 2026-08-13 |
そのまま実行できるコマンド
- 対象
- SQL Server 2008 R2 SP2 / 2012 SP1 以降
- 権限
- 対象DBへの接続権限+対象オブジェクトのメタデータ可視性
- 変更作業
- なし(参照のみ)
- Production実行
- 可能
-- 対象: SQL Server 2008 R2 SP2 / 2012 SP1 以降
-- 権限: 対象DBへの接続権限(sys.stats はメタデータ可視性ルールに従う)
-- 変更作業: なし(参照のみ)
-- Production 実行: 可能
SELECT
SCHEMA_NAME(o.schema_id) AS schema_name,
o.name AS table_name,
s.name AS stats_name,
s.auto_created AS is_auto_created,
STATS_DATE(s.object_id, s.stats_id) AS last_updated,
sp.rows AS table_rows,
sp.rows_sampled AS rows_sampled,
sp.modification_counter AS rows_modified_since_update
FROM sys.stats AS s
INNER JOIN sys.objects AS o
ON s.object_id = o.object_id
CROSS APPLY sys.dm_db_stats_properties(s.object_id, s.stats_id) AS sp
WHERE o.is_ms_shipped = 0 -- システムオブジェクトを除外
AND o.type IN ('U', 'V') -- ユーザーテーブルとビュー
ORDER BY sp.modification_counter DESC, last_updated ASC;`CROSS APPLY` は統計が無効なオブジェクトの行を落とすため、統計が1つも無いテーブルは結果に現れません。テーブルの網羅性を優先する場合は `OUTER APPLY` に変えてください。
- 対象
- SQL Server 2008(RTM〜SP1 を含む)
- 権限
- 対象DBへの接続権限+対象オブジェクトのメタデータ可視性
- 変更作業
- なし(参照のみ)
- Production実行
- 可能
-- 対象: SQL Server 2008(sys.dm_db_stats_properties が使えない環境)
-- 権限: 対象DBへの接続権限
-- 変更作業: なし(参照のみ)
-- Production 実行: 可能
SELECT
SCHEMA_NAME(o.schema_id) AS schema_name,
o.name AS table_name,
s.name AS stats_name,
s.auto_created AS is_auto_created,
s.no_recompute AS is_no_recompute,
STATS_DATE(s.object_id, s.stats_id) AS last_updated
FROM sys.stats AS s
INNER JOIN sys.objects AS o
ON s.object_id = o.object_id
WHERE o.is_ms_shipped = 0
AND o.type IN ('U', 'V')
ORDER BY last_updated ASC;この版では変更行数を取得できません。更新の要否は行数の増減やジョブ実行履歴と併せて判断します。
- 対象
- SQL Server 2008 以降
- 権限
- 対象テーブルの所有者、または db_owner / db_ddladmin
- 変更作業
- あり(統計の再作成とプランキャッシュの再コンパイル)
- Production実行
- 実行可能だが、負荷の低い時間帯を選ぶこと
-- 対象: SQL Server 2008 以降
-- 権限: 対象テーブルの所有者 または db_owner / db_ddladmin
-- 変更作業: あり(統計更新 → 該当オブジェクトのプラン再コンパイル)
-- Production 実行: 可能だが I/O とCPUを消費するため時間帯を選ぶこと
UPDATE STATISTICS [dbo].[SampleTable] ([IX_SampleTable_01])
WITH FULLSCAN;`WITH FULLSCAN` は全行を読むため大きなテーブルでは I/O が跳ね上がります。まずは既定のサンプリング(オプション省略)で更新し、推定行数が改善しない場合に `FULLSCAN` を検討してください。
結果の読み方
| 列 | 意味 | 確認するポイント |
|---|---|---|
| schema_name | スキーマ名 | 対象スキーマが想定どおりか |
| table_name | テーブル名 | 行数の多いテーブルから優先的に確認する |
| stats_name | 統計名 | インデックス統計(インデックス名と同名)と自動生成統計(`_WA_Sys_` で始まる)を区別する |
| is_auto_created | 自動生成された統計か | 1 なら AUTO_CREATE_STATISTICS による自動生成 |
| last_updated | 最終更新日時 | NULL(未更新)や、明らかに古い日付が無いか |
| table_rows | 統計が対象とする行数 | 実際の行数と大きく乖離していないか |
| rows_sampled | サンプリングされた行数 | `table_rows` に対して極端に少ない場合、推定精度が落ちる |
| rows_modified_since_update | 最終更新以降に変更された行数 | 大きい行が更新候補。値が行数に対して大きいほど推定がずれやすい |
こういう状況で使います
- 同じクエリなのに、ある日から急に実行時間が伸びた
- 実行プランの推定行数と実際の行数が大きく食い違う
- 大量のデータ投入・削除を行った直後から特定の処理だけ遅い
- 統計の自動更新に任せているが、実際にいつ更新されたのか分からない
考えられる原因(可能性の高い順)
01
自動更新のしきい値に達していない
AUTO_UPDATE_STATISTICS は、変更行数がしきい値を超えたときに次回そのオブジェクトを参照した時点で更新されます。行数の多いテーブルほどしきい値に達しにくく、変更が積み上がったまま古い統計で実行プランが作られることがあります。
02
サンプリング率が低く、分布が実態とずれている
既定のサンプリングは全行を読みません。値の偏りが大きい列では、サンプリング結果から推定した行数が実際と乖離することがあります。
03
統計の自動更新が無効化されている
データベースオプションの AUTO_UPDATE_STATISTICS が OFF、または個々の統計に NO_RECOMPUTE が設定されていると、明示的に更新するまで古いままになります。
04
定期メンテナンスジョブが失敗している
インデックス再構築や統計更新のジョブがエラーで停止していると、更新日時がジョブ停止日で止まります。
確認手順
- 1
統計の最終更新日時を一覧化する
参照のみ上記の参照専用SQLを実行し、`last_updated` が古い、または `rows_modified_since_update` が大きい統計を洗い出します。
- 2
データベースオプションを確認する
参照のみ`SELECT name, is_auto_update_stats_on, is_auto_create_stats_on, is_auto_update_stats_async_on FROM sys.databases;` で自動更新の設定を確認します。
- 3
NO_RECOMPUTE が付いた統計を洗い出す
参照のみ`SELECT name FROM sys.stats WHERE no_recompute = 1;` で自動更新の対象外になっている統計を確認します。
- 4
該当クエリの実行プランで推定行数と実行時行数を比較する
低実際の実行プランを取得し、推定行数と実際の行数の差が大きい演算子を特定します。差が小さければ原因は統計ではありません。
対応方法
すぐに実施できる低リスクの対応
更新候補を特定して個別に統計を更新する
中`rows_modified_since_update` が大きい統計だけを `UPDATE STATISTICS <table> (<stats>)` で更新します。対象を絞れば負荷を抑えられます。
自動更新の設定を確認して有効化する
中AUTO_UPDATE_STATISTICS が OFF なら、変更影響を確認したうえで有効化を検討します。設定変更はデータベース単位に効きます。
事前検討が必要な変更
サンプリング率を上げる
中偏りの大きい列は `WITH FULLSCAN` または `WITH SAMPLE n PERCENT` を指定します。実行時間と I/O が増えるため、対象と時間帯を決めてから適用します。
統計更新を定期ジョブ化する
中変更行数を条件に更新対象を選ぶジョブを組み、実行結果の成否を監視対象に含めます。
再起動・サービス影響を伴う変更
データベース全体の統計を一括更新する
高`EXEC sp_updatestats;` はデータベース全体を対象にするため I/O とCPUを大きく消費し、実行中は他の処理に影響します。メンテナンス時間帯以外では実施しないでください。
インデックスの再構築で統計を作り直す
高インデックス再構築は統計を FULLSCAN 相当で更新しますが、ロックとログ増加を伴います。ONLINE オプションの可否はエディションに依存します。
!注意事項
- `sp_updatestats` とインデックス全体の再構築は、データベース全体に負荷をかける「大規模な統計更新」に該当します。実行時間帯とトランザクションログの空き容量を事前に確認してください。
- 統計を更新すると、そのオブジェクトを参照する実行プランが再コンパイルされます。更新直後に一時的なCPU上昇が起きることがあります。
- `WITH FULLSCAN` は全行スキャンです。テラバイト級のテーブルでは実行時間が長時間に及ぶ場合があります。
- 推定行数と実際の行数の差が小さいなら、遅延の原因は統計ではありません。統計更新を繰り返しても改善しません。
バージョン・環境による違い
これで解決しない場合に確認すること
実行プランの推定行数と実際の行数を比較する
差が10倍以上ある演算子があるかを確認します。差が無い場合、統計以外(インデックス設計、パラメータスニッフィング、待機事象)を疑います。
待機事象を確認する
CPU 待ちなのか I/O 待ちなのかロック待ちなのかで対処が変わります。CXPACKET が支配的な場合は並列度の確認に進みます。
パラメータスニッフィングの影響を確認する
同じクエリでもパラメータ値によって実行時間が大きく変わる場合、統計ではなくプラン再利用の問題である可能性があります。
メンテナンスジョブの実行履歴を確認する
`msdb.dbo.sysjobhistory` を参照し、統計更新ジョブが失敗していないかを確認します。
この文書の根拠と限界
製品の公式ドキュメントに基づく説明
SQL Server のカタログビュー `sys.stats`、関数 `STATS_DATE()`、動的管理関数 `sys.dm_db_stats_properties` の公開仕様に基づく一般的な確認手順です。特定の顧客環境の設定値や実測値は含みません。
よくある質問
Productionで実行できますか?
参照用のSQL(1つ目・2つ目のコードブロック)はデータも設定も変更しないため、本番環境で実行できます。`UPDATE STATISTICS` を含む3つ目は変更を伴うため、対象と時間帯を決めてから実行してください。
AWS RDSでも使用できますか?
使用できます。`sys.stats`、`STATS_DATE()`、`sys.dm_db_stats_properties` はいずれも Amazon RDS for SQL Server で利用可能です。OSレベルの権限は必要ありません。
どの権限が必要ですか?
対象データベースへの接続権限があれば実行できます。ただし `sys.stats` はメタデータ可視性ルールに従うため、権限のないオブジェクトの統計は結果に現れません。全オブジェクトを見たい場合は、対象DBで十分な参照権限を持つアカウントを使ってください。
結果をどう判断しますか?
`last_updated` が古いことだけを理由に更新する必要はありません。`rows_modified_since_update` が行数に対して大きい統計が優先度の高い更新候補です。更新日時が古くてもデータが変わっていなければ、統計は現状のままで妥当です。
統計を更新すれば遅いクエリは必ず速くなりますか?
いいえ。統計更新が効くのは、推定行数と実際の行数が乖離していた場合です。乖離が無い場合は、インデックス設計、待機事象、並列度など別の要因を確認してください。
この文書がカバーする質問
- SQL Serverで統計情報の最終更新日時を確認したい
- STATS_DATE の使い方が知りたい
- テーブルごとに統計がいつ更新されたか一覧で出したい
リスク表示の意味
- 参照のみデータと設定を変更しません。
- 低影響は限定的ですが、権限と負荷の確認が必要です。
- 中性能・ロック・コストに影響する可能性があります。
- 高障害・データ損失・復旧作業が発生する可能性があります。
- 専門家レビュー必須本番適用前に別途レビューが必須です。
GIIPの対応範囲
この確認を一度実行すること自体は難しくありません。しかし、複数のSQL ServerやAurora MySQL環境で継続的に状態を確認し、悪化の兆候が出た時点で対応するには運用体制が必要です。GIIPではAWSとAzure上の複数データベースおよび約30のWebサービスを、AIエージェントと人間の専門家が継続的に監視しています。統計の更新状況のように「一度見れば分かるが、見続けるのが難しい」項目を定常監視に組み込み、変化が出たときだけ人が判断する形にしています。
執筆・技術検証
GIIP プロダクション運用チーム
大規模Webサービス、SQL Server、Oracle、AWS、Azureの設計・移行・運用に約30年従事。x12largeクラスのAWS RDS for SQL Server環境12セット、約12万テーブルのOracle環境、約3TBのTiDBからAurora MySQLへの移行を経験。現在も複数のクラウドデータベースと約30のWebサービスを、AIエージェントと人間の専門家が継続的に監視・運用しています。
SQL ServerでCXPACKET待機が多いときの原因と確認方法
sys.dm_os_wait_stats と sys.dm_os_waiting_tasks で CXPACKET を評価し、CXCONSUMER との区別を踏まえて並列度の見直しに進むための手順です。
sql-serverSQL ServerのMAXDOPとCost Threshold for Parallelismを確認・変更する方法
サーバー・データベース・クエリの3階層で並列度を確認し変更する手順と、それぞれの適用範囲・影響範囲の違いを整理します。
sql-serverSQL Serverで長時間開いたままのトランザクションを確認するSQL
sys.dm_tran_active_transactions 系のDMVで、開始時刻・セッション・最後に実行したSQLまで含めて放置トランザクションを特定する手順です。
関連サービス
SQL Serverの性能問題を相談する
同じ確認を複数の環境で継続する必要がある場合は、運用体制ごと相談できます。
SQL Serverの性能問題を相談する