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

Tho*_*rik 48 index sql-server statistics index-tuning sql-server-2012

上周我一直在做 MS10775A 课程,出现了一个培训师无法可靠回答的问题是:

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

我们发现网上的讨论都在争论它有没有。

Mic*_*Sim 61

在关心更新统计信息时,您可以记住以下几点(复制自重建索引与更新统计信息 (Benjamin Nevarez))

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

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

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

  4. 使用手动创建的统计信息CREATE STATISTICS不会被任何ALTER INDEX ... REBUILD操作更新,包括ALTER TABLE ... REBUILD. ALTER TABLE ... REBUILD如果在正在重建的表上定义了聚集索引的统计信息,则它会更新聚集索引的统计信息。

  5. 重新组织索引,例如 usingALTER 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;
Run Code Online (Sandbox Code Playgroud)

此查询显示每个 stats 对象上次更新的日期:

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';
Run Code Online (Sandbox Code Playgroud)

结果显示尚未发生任何更新,这是正确的,因为我们刚刚创建了表:

?????????????????????????????????????????????
? 对象名称 ? 统计名称 ? 统计日期?
?????????????????????????????????????????????
? dbo.SomeTable ?CX ? 空值 ?
? dbo.SomeTable ?一世 ?空值 ?
? dbo.SomeTable ?pk ? 空值 ?
? dbo.SomeTable ?d ? 空值 ?
?????????????????????????????????????????????

让我们重建整个表,看看是否更新了统计信息:

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';
Run Code Online (Sandbox Code Playgroud)
?????????????????????????????????????????????????????? ?????
? 对象名称 ? 统计名称 ? 统计日期?
?????????????????????????????????????????????????????? ?????
? dbo.SomeTable ?CX ? 2018-09-17 14:09:13.590?
? dbo.SomeTable ?一世 ?空值 ?
? dbo.SomeTable ?pk ? 空值 ?
? dbo.SomeTable ?d ? 空值 ?
?????????????????????????????????????????????????????? ?????

结果显示只更新了聚集索引统计信息。

接下来,我们执行离散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';
Run Code Online (Sandbox Code Playgroud)

如您所见,我们刚刚更新了该d列的统计信息:

?????????????????????????????????????????????????????? ?????
? 对象名称 ? 统计名称 ? 统计日期?
?????????????????????????????????????????????????????? ?????
? dbo.SomeTable ?CX ? 2018-09-17 14:09:13.590?
? dbo.SomeTable ?一世 ?空值 ?
? dbo.SomeTable ?pk ? 空值 ?
? 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';
Run Code Online (Sandbox Code Playgroud)
?????????????????????????????????????????????????????? ?????
? 对象名称 ? 统计名称 ? 统计日期?
?????????????????????????????????????????????????????? ?????
? 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 ? 2018-09-17 14:09:13.607 ?
?????????????????????????????????????????????????????? ?????

如您所见,确定所有统计信息都已更新的唯一方法是手动更新每个统计信息,或使用UPDATE STATISTICS (table);.


小智 9

SQL Server 统计信息的 Microsoft Docs 页面指出

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