giip
SES Proposal
SQL Server統計情報性能インデックス

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

そのまま実行できるコマンド

Last-updated time and modified row count per statistic (recommended for SQL Server 2012 SP1 and later)参照のみ
対象
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 (environments without `sys.dm_db_stats_properties`)参照のみ
対象
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.

Update a specific statistic only (involves a change, so review beforehand)
対象
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_nameSchema nameWhether the target schema is as expected
table_nameTable namePrioritize tables with the largest row counts
stats_nameStatistics nameDistinguish index statistics (same name as the index) from auto-created statistics (prefixed with `_WA_Sys_`)
is_auto_createdWhether the statistic was auto-created1 means it was auto-created by AUTO_CREATE_STATISTICS
last_updatedLast-updated timeCheck for NULL (never updated) or a suspiciously old date
table_rowsRow count covered by the statisticWhether it diverges significantly from the actual row count
rows_sampledNumber of rows sampledIf extremely low relative to `table_rows`, estimation accuracy suffers
rows_modified_since_updateRows modified since the last updateLarger 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

考えられる原因(可能性の高い順)

  1. 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.

  2. 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.

  3. 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.

  4. 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. 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. 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. 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. 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.

バージョン・環境による違い

SQL Server 2008`sys.dm_db_stats_properties` was added in 2008 R2 SP2 / 2012 SP1. On earlier versions, you can only get the last-updated time via `STATS_DATE()`.
Amazon RDS for SQL Server`sys.stats` and `STATS_DATE()` work as-is on the managed instance; no OS-level operations are needed.
SQL Server 2016 and laterFrom compatibility level 130 onward, the auto-update threshold calculation changed, making updates trigger more easily even on large tables than before.

これで解決しない場合に確認すること

  • 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エージェントと人間の専門家が継続的に監視・運用しています。

関連するナレッジ

関連サービス

Get advice on SQL Server performance issues

同じ確認を複数の環境で継続する必要がある場合は、運用体制ごと相談できます。

Get advice on SQL Server performance issues

ナレッジベース一覧へ