web-dev-qa-db-ja.com

再インデックスは統計を更新しますか?

私はこの1週間、MS10775Aコースを行ってきましたが、トレーナーが確実に回答できないという疑問が生じました。

再インデックスは統計を更新しますか?

オンラインでディスカッションが行われ、行われていることと行われていないことの両方が議論されました。

44
Thor Erik

統計の更新を気にするときは、次の点に注意してください( インデックスの再構築と統計の更新(Benjamin Nevarez)からコピー]

  1. デフォルトでは、_UPDATE STATISTICS_ステートメントはテーブルのレコードのサンプルのみを使用します。 _UPDATE STATISTICS WITH FULLSCAN_を使用すると、テーブル全体がスキャンされます。

  2. デフォルトでは、_UPDATE STATISTICS_ステートメントはインデックスと列の両方の統計を更新します。 COLUMNSオプションを使用すると、列統計のみが更新されます。 INDEXオプションを使用すると、インデックス統計のみが更新されます。

  3. たとえば、_ALTER INDEX … REBUILD_を使用してインデックスを再構築すると、_WITH FULLSCAN_unlessテーブルはパーティション化されます。この場合、統計はサンプリングされるだけです (SQL Server 2012以降に適用されます)。

  4. _CREATE STATISTICS_を使用して手動で作成された統計は、_ALTER INDEX ... REBUILD_を含む_ALTER TABLE ... REBUILD_操作によって更新されません。 _ALTER TABLE ... REBUILD_は、再構築中のテーブルで統計が定義されている場合、クラスター化インデックスの統計を更新します。

  5. たとえば、_ALTER INDEX … REORGANIZE_を使用してインデックスを再編成しても、統計は更新されません。

短い答えは、_UPDATE STATISTICS_を使用して列統計を更新する必要があること、およびインデックスの再構築ではインデックス統計のみが更新されるということです。 UPDATE STATISTICS (tablename) WITH FULLSCAN;構文を使用すると、インデックス統計や手動で作成された統計を含む、テーブルのすべての統計を強制的に更新できます。

次のコードは、上記でカプセル化されたルールを示しています。

最初に、いくつかの列とクラスター化インデックスを持つテーブルを作成します。

_USE tempdb;

IF OBJECT_ID(N'dbo.SomeTable', N'U') IS NOT NULL
DROP TABLE dbo.SomeTable;

CREATE TABLE dbo.SomeTable
(
    rn int NOT NULL IDENTITY(1,1)
        CONSTRAINT pk
        PRIMARY KEY NONCLUSTERED
    , i int NOT NULL INDEX i 
    , d sysname NOT NULL
) ON [PRIMARY] WITH (DATA_COMPRESSION = NONE);

CREATE UNIQUE CLUSTERED INDEX cx ON dbo.SomeTable (i, d);

CREATE STATISTICS d ON dbo.SomeTable (d) WITH FULLSCAN;

INSERT INTO dbo.SomeTable (d, i)
SELECT c1.name, c1.id
FROM sys.syscolumns c1;
_

このクエリは、各統計オブジェクトが最後に更新された日付を示します。

_SELECT ObjectName = sc.name + N'.' + o.name
    , StatsName = s.name
    , StatsDate = STATS_DATE(s.object_id, s.stats_id)
FROM sys.stats s
    INNER JOIN sys.objects o ON s.object_id = o.object_id
    INNER JOIN sys.schemas sc ON o.schema_id = sc.schema_id
WHERE sc.name = N'dbo'
    AND o.name = N'SomeTable';
_

結果は、更新がまだ行われていないことを示しています。これは、テーブルを作成したばかりなので正しいことです。

╔═══════════════╦═══════════╦═══════════╗
║ObjectName║StatsName ║StatsDate║
╠═══════════════╬═══════════╬═══════════╣
║dbo.SomeTable║cx║NULL║
║dbo.SomeTable║i║NULL║
║dbo.SomeTable║pk║NULL║
║dbo.SomeTable ║d║NULL。
╚═══════════════╩═══════════╩═══════════ ╝

テーブル全体を再構築して、統計が更新されるかどうかを確認してみましょう。

_ALTER TABLE dbo.SomeTable REBUILD;

SELECT ObjectName = sc.name + N'.' + o.name
    , StatsName = s.name
    , StatsDate = STATS_DATE(s.object_id, s.stats_id)
FROM sys.stats s
    INNER JOIN sys.objects o ON s.object_id = o.object_id
    INNER JOIN sys.schemas sc ON o.schema_id = sc.schema_id
WHERE sc.name = N'dbo'
    AND o.name = N'SomeTable';
_
╔═══════════════╦═══════════╦═════════════════════ ════╗
║ObjectName║StatsName║StatsDate║
╠═══════════════╬═══════════ ╬═════════════════════════╣
║dbo.SomeTable║cx║2018-09-17 14:09:13.590║ 
║dbo.SomeTable║i║NULL║
║dbo.SomeTable║pk║NULL║
║dbo.SomeTable║d║NULL║
╚══ ═════════════╩═══════════╩════════════════════════ ═╝

結果は、clustered index統計のみが更新されたことを示しています。

次に、個別の_UPDATE STATS_操作を実行します。

_UPDATE STATISTICS dbo.SomeTable(d) WITH FULLSCAN;

SELECT ObjectName = sc.name + N'.' + o.name
    , StatsName = s.name
    , StatsDate = STATS_DATE(s.object_id, s.stats_id)
FROM sys.stats s
    INNER JOIN sys.objects o ON s.object_id = o.object_id
    INNER JOIN sys.schemas sc ON o.schema_id = sc.schema_id
WHERE sc.name = N'dbo'
    AND o.name = N'SomeTable';
_

ご覧のとおり、d列の統計情報を更新しました。

╔═══════════════╦═══════════╦═════════════════════ ════╗
║ObjectName║StatsName║StatsDate║
╠═══════════════╬═══════════ ╬═════════════════════════╣
║dbo.SomeTable║cx║2018-09-17 14:09:13.590║ 
║dbo.SomeTable║i║NULL║
║dbo.SomeTable║pk║NULL║
║dbo.SomeTable║d║2018-09-17 14:09:13.597 ║
╚═══════════════╩═══════════╩═══════════════ ══════════╝

次に、テーブル全体の統計を更新します。

_UPDATE STATISTICS dbo.SomeTable WITH FULLSCAN;

SELECT ObjectName = sc.name + N'.' + o.name
    , StatsName = s.name
    , StatsDate = STATS_DATE(s.object_id, s.stats_id)
FROM sys.stats s
    INNER JOIN sys.objects o ON s.object_id = o.object_id
    INNER JOIN sys.schemas sc ON o.schema_id = sc.schema_id
WHERE sc.name = N'dbo'
    AND o.name = N'SomeTable';
_
╔═══════════════╦═══════════╦═════════════════════ ════╗
║ObjectName║StatsName║StatsDate║
╠═══════════════╬═══════════ ╬═════════════════════════╣
║dbo.SomeTable║cx║2018-09-17 14:09:13.600║ 
║dbo.SomeTable║i║2018-09-17 14:09:13.600║
║dbo.SomeTable║pk║2018-09-17 14:09:13.603║
║dbo.SomeTable║d║2018-09-17 14:09:13.607║
╚═══════════════╩═════════ ══╩═════════════════════════╝

ご覧のように、すべての統計が確実に更新される唯一の方法は、それぞれを手動で更新するか、またはUPDATE STATISTICS (table);でテーブル全体を更新することです。

53
MicSim

SQL Server統計のMicrosoft Docsページ states

インデックスの再構築、デフラグ、再編成などの操作によって、データの分布が変わることはありません。したがって、ALTER INDEX REBUILD、DBCC DBREINDEX、DBCC INDEXDEFRAG、またはALTER INDEX REORGANIZE操作の実行後に統計を更新する必要はありません。 Query Optimizerは、ALTER INDEX REBUILDまたはDBCC DBREINDEXを使用してテーブルまたはビューのインデックスを再構築すると統計を更新しますが、この統計の更新はインデックスの再作成の副産物です。クエリオプティマイザーは、DBCC INDEXDEFRAGまたはALTER INDEX REORGANIZE操作の後に統計を更新しません。

6
bside