SQL to Check Statistics Last-Updated Time per Table in SQL Server
公開日 2026-08-13 · 更新日 2026-08-13 · 最終検証日 2026-08-13
結論
In SQL Server, you can check the last-updated time of statistics per table and per statistic using `sys.stats` and `STATS_DATE()`. On SQL Server 2008 R2 SP2 / 2012 SP1 and later, you can also use `sys.dm_db_stats_properties` to retrieve the number of rows modified since the last update (`modification_counter`) at the same time. The SQL below is a read-only query that does not change any data or settings, and can be run as-is even in production.
この文書の適用条件
| 対象製品 | SQL Server / Amazon RDS for SQL Server / Azure SQL Managed Instance |
|---|---|
| 確認バージョン | SQL Server 2008 and later (`sys.dm_db_stats_properties` requires 2008 R2 SP2 / 2012 SP1 or later) |
| 適用環境 | On-premises, EC2, Amazon RDS, Azure |
| 必要権限 | Connection permission to the target database, plus metadata visibility for the target objects (`sys.stats` follows metadata visibility rules, so objects you lack permission on are not returned as rows) |
| 実行影響 | The check SQL is read-only (no data or settings are changed). The `UPDATE STATISTICS` shown as a remediation step involves rebuilding statistics and recompiling plans. |
| 再起動 | Not required |
| 最終検証日 | 2026-08-13 |
そのまま実行できるコマンド
- 対象
- SQL Server 2008 R2 SP2 / 2012 SP1 and later
- 権限
- Connection permission to the target DB + metadata visibility for the target objects
- 変更作業
- None (read-only)
- Production実行
- Safe to run
-- 対象: 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` excludes rows for objects with no statistics, so tables with zero statistics will not appear in the results. If you need full table coverage, change it to `OUTER APPLY`.
- 対象
- SQL Server 2008 (including RTM through SP1)
- 権限
- Connection permission to the target DB
- 変更作業
- None (read-only)
- Production実行
- Safe to run
-- 対象: 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;This version cannot retrieve the modified row count. Judge whether an update is needed based on row-count changes and job execution history instead.
- 対象
- SQL Server 2008 and later
- 権限
- Owner of the target table, or db_owner / db_ddladmin
- 変更作業
- Yes (rebuilds the statistic and recompiles plans for the object)
- Production実行
- Can be run, but choose a low-load time window
-- 対象: SQL Server 2008 以降
-- 権限: 対象テーブルの所有者 または db_owner / db_ddladmin
-- 変更作業: あり(統計更新 → 該当オブジェクトのプラン再コンパイル)
-- Production 実行: 可能だが I/O とCPUを消費するため時間帯を選ぶこと
UPDATE STATISTICS [dbo].[SampleTable] ([IX_SampleTable_01])
WITH FULLSCAN;`WITH FULLSCAN` reads every row, so I/O spikes on large tables. Update with default sampling first (omit the option), and consider `FULLSCAN` only if the estimated row count does not improve.
結果の読み方
| 列 | 意味 | 確認するポイント |
|---|---|---|
| schema_name | Schema name | Whether the target schema is as expected |
| table_name | Table name | Prioritize tables with the largest row counts |
| stats_name | Statistics name | Distinguish index statistics (same name as the index) from auto-created statistics (prefixed with `_WA_Sys_`) |
| is_auto_created | Whether the statistic was auto-created | 1 means it was auto-created by AUTO_CREATE_STATISTICS |
| last_updated | Last-updated time | Check for NULL (never updated) or a suspiciously old date |
| table_rows | Row count covered by the statistic | Whether it diverges significantly from the actual row count |
| rows_sampled | Number of rows sampled | If extremely low relative to `table_rows`, estimation accuracy suffers |
| rows_modified_since_update | Rows modified since the last update | Larger values are stronger candidates for an update; the larger this is relative to the row count, the more the estimate can drift |
こういう状況で使います
- The same query suddenly started taking longer from a certain day
- The estimated row count in the execution plan differs greatly from the actual row count
- Only a specific process has been slow right after a large data load or deletion
- Statistics are left to auto-update, but it is unclear when they were actually last updated
考えられる原因(可能性の高い順)
01
The auto-update threshold has not been reached
AUTO_UPDATE_STATISTICS updates an object the next time it is referenced after the number of modified rows exceeds the threshold. Tables with more rows take longer to reach the threshold, so changes can accumulate while execution plans are still built from outdated statistics.
02
Sampling rate is low and the distribution no longer matches reality
Default sampling does not read every row. For columns with highly skewed values, the row-count estimate derived from sampling can diverge from the actual data.
03
Automatic statistics updates are disabled
If the AUTO_UPDATE_STATISTICS database option is OFF, or NO_RECOMPUTE is set on individual statistics, they remain stale until explicitly updated.
04
A scheduled maintenance job is failing
If an index rebuild or statistics update job stops due to an error, the last-updated time stays frozen at the date the job stopped.
確認手順
- 1
List the last-updated time of statistics
参照のみRun the read-only SQL above to find statistics where `last_updated` is old or `rows_modified_since_update` is large.
- 2
Check the database options
参照のみRun `SELECT name, is_auto_update_stats_on, is_auto_create_stats_on, is_auto_update_stats_async_on FROM sys.databases;` to check the auto-update settings.
- 3
Find statistics with NO_RECOMPUTE set
参照のみRun `SELECT name FROM sys.stats WHERE no_recompute = 1;` to find statistics excluded from auto-update.
- 4
Compare estimated vs. actual row counts in the query execution plan
低Capture the actual execution plan and identify operators where the estimated and actual row counts differ significantly. If the difference is small, statistics are not the cause.
対応方法
すぐに実施できる低リスクの対応
Identify update candidates and update their statistics individually
中Update only the statistics where `rows_modified_since_update` is large, using `UPDATE STATISTICS <table> (<stats>)`. Narrowing the target keeps the load manageable.
Check and enable auto-update settings
中If AUTO_UPDATE_STATISTICS is OFF, review the impact of the change and consider enabling it. The setting applies at the database level.
事前検討が必要な変更
Increase the sampling rate
中For highly skewed columns, specify `WITH FULLSCAN` or `WITH SAMPLE n PERCENT`. This increases execution time and I/O, so apply it only after deciding on the target and time window.
Turn statistics updates into a scheduled job
中Build a job that selects update targets based on the number of modified rows, and include its success/failure in your monitoring.
再起動・サービス影響を伴う変更
Bulk-update statistics for the entire database
高`EXEC sp_updatestats;` targets the whole database, consuming significant I/O and CPU, and affects other processing while it runs. Do not run it outside a maintenance window.
Rebuild indexes to regenerate statistics
高An index rebuild updates statistics at a level equivalent to FULLSCAN, but it involves locking and increased log growth. Whether the ONLINE option is available depends on the edition.
!注意事項
- `sp_updatestats` and a full index rebuild both count as "large-scale statistics updates" that load the entire database. Check the execution time window and available transaction log space beforehand.
- Updating statistics recompiles execution plans that reference the object. A temporary CPU spike can occur right after the update.
- `WITH FULLSCAN` is a full table scan. On terabyte-scale tables, it can take a long time to complete.
- If the difference between estimated and actual row counts is small, statistics are not the cause of the slowdown, and repeated statistics updates will not help.
バージョン・環境による違い
これで解決しない場合に確認すること
Have you compared estimated vs. actual row counts in the execution plan?
Check whether any operator shows a 10x or greater difference. If there is no such difference, suspect something other than statistics — index design, parameter sniffing, or wait statistics.
Have you checked wait statistics?
The remediation differs depending on whether the wait is CPU, I/O, or lock related. If CXPACKET dominates, move on to checking the degree of parallelism.
Have you checked for parameter sniffing effects?
If the same query's execution time varies greatly depending on the parameter value, the issue may be plan reuse rather than statistics.
Have you checked the execution history of maintenance jobs?
Check `msdb.dbo.sysjobhistory` to confirm the statistics-update job has not been failing.
この文書の根拠と限界
製品の公式ドキュメントに基づく説明
A general procedure based on the public specifications of the SQL Server catalog view `sys.stats`, the function `STATS_DATE()`, and the dynamic management function `sys.dm_db_stats_properties`. It does not include configuration values or measurements from any specific customer environment.
よくある質問
Can this be run in production?
The read-only SQL (the first and second code blocks) does not change any data or settings, so it can be run in production. The third block, which includes `UPDATE STATISTICS`, involves a change, so decide on the target and time window before running it.
Does this work on AWS RDS?
Yes. `sys.stats`, `STATS_DATE()`, and `sys.dm_db_stats_properties` are all available on Amazon RDS for SQL Server. No OS-level permissions are required.
What permissions are required?
Connection permission to the target database is enough to run it. However, `sys.stats` follows metadata visibility rules, so statistics on objects you lack permission on will not appear in the results. To see every object, use an account with sufficient read permission on the target DB.
How should I interpret the results?
An old `last_updated` value alone is not a reason to update. Statistics where `rows_modified_since_update` is large relative to the row count are the higher-priority update candidates. If the data has not changed, statistics with an old last-updated time can still be valid as they are.
Will updating statistics always make a slow query faster?
No. Updating statistics helps only when the estimated and actual row counts diverge. If there is no divergence, check other factors such as index design, wait statistics, or the degree of parallelism.
この文書がカバーする質問
- I want to check the last-updated time of statistics in SQL Server
- I want to know how to use STATS_DATE
- I want a list of when statistics were last updated per table
リスク表示の意味
- 参照のみデータと設定を変更しません。
- 低影響は限定的ですが、権限と負荷の確認が必要です。
- 中性能・ロック・コストに影響する可能性があります。
- 高障害・データ損失・復旧作業が発生する可能性があります。
- 専門家レビュー必須本番適用前に別途レビューが必須です。
GIIPの対応範囲
Running this check once is not difficult. But continuously checking the status across multiple SQL Server or Aurora MySQL environments, and responding the moment signs of degradation appear, requires an operational framework. At GIIP, AI agents and human experts continuously monitor multiple databases on AWS and Azure along with roughly 30 web services. Items like statistics freshness — easy to check once but hard to keep watching — are built into routine monitoring, with a human judgment call triggered only when something changes.
執筆・技術検証
GIIP プロダクション運用チーム
大規模Webサービス、SQL Server、Oracle、AWS、Azureの設計・移行・運用に約30年従事。x12largeクラスのAWS RDS for SQL Server環境12セット、約12万テーブルのOracle環境、約3TBのTiDBからAurora MySQLへの移行を経験。現在も複数のクラウドデータベースと約30のWebサービスを、AIエージェントと人間の専門家が継続的に監視・運用しています。
Causes and Checks for High CXPACKET Waits in SQL Server
A procedure for evaluating CXPACKET using sys.dm_os_wait_stats and sys.dm_os_waiting_tasks, distinguishing it from CXCONSUMER, and moving toward a review of the degree of parallelism.
sql-serverHow to Check and Change MAXDOP and Cost Threshold for Parallelism in SQL Server
A procedure for checking and changing the degree of parallelism at the server, database, and query levels, along with the differences in scope and impact at each level.
sql-serverSQL to Check for Long-Running Open Transactions in SQL Server
A procedure using the sys.dm_tran_active_transactions family of DMVs to identify abandoned transactions, including start time, session, and the last SQL statement executed.
関連サービス
Get advice on SQL Server performance issues
同じ確認を複数の環境で継続する必要がある場合は、運用体制ごと相談できます。
Get advice on SQL Server performance issues