重新索引会更新统计信息吗?


43

过去一周,我一直在做MS10775A课程,但培训师无法可靠回答的一个问题是:

重新索引会更新统计信息吗?

我们在网上找到了讨论它是否可行的讨论。


值得注意的是,a REINDEX确实会更新列统计信息,这是重建索引的副作用-您无需更新统计信息。表中的数据不变。这是相同的数据,只是a)移动了它在旋转盘上的位置(重组页面时),或b)坐在了不同的页面中(对于重新构建)。所以:重新索引确实会更新(某些)统计信息:不需要这样做。
伊恩·博伊德

Answers:


51

在关注更新统计信息时,请牢记以下几点(摘自“ 重建索引与更新统计信息”(本杰明·内瓦雷斯)

  1. 默认情况下,该UPDATE STATISTICS语句仅使用表记录的样本。使用UPDATE STATISTICS WITH FULLSCAN将扫描整个表。

  2. 默认情况下,该UPDATE STATISTICS语句同时更新索引和列统计信息。使用该COLUMNS选项将仅更新列统计信息。使用该INDEX选项将仅更新索引统计信息。

  3. 重建索引(例如,通过使用)ALTER INDEX … REBUILD也将以等同于使用的方式更新索引统计信息,WITH FULLSCAN 除非 对该表进行了分区,在这种情况下,仅对统计信息进行采样(适用于SQL Server 2012及更高版本)。

  4. 使用手动创建的统计信息CREATE STATISTICS不会通过任何ALTER INDEX ... REBUILD操作更新,包括ALTER TABLE ... REBUILDALTER 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║
bo 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║
bo dbo.SomeTable║d║NULL║
╚═══════════════牛皮═══════════牛皮═════════════════════ ════╝

结果显示仅聚集索引统计信息已更新。

接下来,我们执行离散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║║-09- 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║我║2018-09-17 14:09:13.600║
║dbo.SomeTable║pk║2018-09-17 14:09:13.603║
║dbo.SomeTable d║-09- 2018-09-17 14:09:13.607║
╚═══════════════牛皮═══════════牛皮═════════════════════ ════╝

如您所见,确定所有统计信息均得到更新的唯一方法是手动更新每个统计信息,或使用来更新整个表格UPDATE STATISTICS (table);


@JeremyWeir-从我刚刚添加到上述问题的示例代码中可以看到,唯一更新的统计信息是通过an ALTER INDEX ... REBUILDUPDATE STATISTICS语句显式更新的那些统计信息。如果重建表本身,则仅更新聚集索引统计信息。仅供参考,同一索引对象不一定支持主键和聚簇索引。
Max Vernon

5

SQL Server统计信息的“ Microsoft文档”页面指出

诸如重建,碎片整理或重组索引之类的操作不会更改数据的分布。因此,在执行ALTER INDEX REBUILD,DBCC DBREINDEX,DBCC INDEXDEFRAG或ALTER INDEX REORGANIZE操作之后您不需要更新统计信息。当您使用ALTER INDEX REBUILD或DBCC DBREINDEX在表或视图上重建索引时,Query Optimizer会更新统计信息,但是,此统计信息更新是重新创建索引的副产品。在执行DBCC INDEXDEFRAG或ALTER INDEX REORGANIZE操作之后,查询优化器不会更新统计信息。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.