Um re-índice atualiza as estatísticas?

43

Eu estive no curso MS10775A na semana passada e uma pergunta que o treinador não conseguiu responder com segurança foi:

Um re-índice atualiza as estatísticas?

Encontramos discussões on-line argumentando que isso acontece e o que não acontece.

Thor Erik
fonte
É útil observar que a REINDEXatualização das estatísticas da coluna é um efeito colateral da reconstrução do índice - você não precisa atualizar as estatísticas. Os dados na tabela não são alterados. São os mesmos dados, apenas a) mudou sua localização na bandeja giratória (quando uma página é reorganizada) ou b) em uma página diferente (no caso de uma recriação). Assim: a re-index faz (alguns) atualizar estatísticas: não há necessidade de fazê-lo.
Ian Boyd

Respostas:

51

Lembre-se do seguinte ao se importar com a atualização de estatísticas (copiada de Reconstruindo índices versus Atualizando estatísticas (Benjamin Nevarez)

  1. Por padrão, a UPDATE STATISTICSinstrução usa apenas uma amostra de registros da tabela. Usar UPDATE STATISTICS WITH FULLSCANirá verificar a tabela inteira.

  2. Por padrão, a UPDATE STATISTICSinstrução atualiza as estatísticas do índice e da coluna. O uso da COLUMNSopção atualizará apenas as estatísticas da coluna. O uso da INDEXopção atualizará apenas as estatísticas do índice.

  3. A reconstrução de um índice , por exemplo, usando ALTER INDEX … REBUILDtambém atualizará as estatísticas do índice com o equivalente a usar WITH FULLSCAN , a menos que a tabela seja particionada; nesse caso, as estatísticas serão apenas amostradas (se aplica ao SQL Server 2012 e posterior).

  4. As estatísticas criadas manualmente usando CREATE STATISTICSnão são atualizadas por nenhuma ALTER INDEX ... REBUILDoperação, inclusive ALTER TABLE ... REBUILD. ALTER TABLE ... REBUILDatualiza estatísticas para o índice em cluster, se um estiver definido na tabela que está sendo reconstruída.

  5. Reorganizar um índice , por exemplo, usar ALTER INDEX … REORGANIZEnão atualiza nenhuma estatística.

A resposta curta é que você precisa usar UPDATE STATISTICSpara atualizar as estatísticas da coluna e que uma reconstrução do índice atualizará apenas as estatísticas do índice. Você pode forçar uma atualização para todas as estatísticas em uma tabela, incluindo estatísticas de índice e estatísticas criadas manualmente, com a UPDATE STATISTICS (tablename) WITH FULLSCAN;sintaxe.

O código a seguir ilustra as regras encapsuladas acima:

Primeiro, criaremos uma tabela com algumas colunas e um índice em cluster:

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;

Esta consulta mostra a data em que cada objeto de estatísticas foi atualizado pela última vez:

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';

Os resultados mostram que ainda não houve atualizações, o que está correto desde que criamos a tabela:

╔═══════════════╦═══════════╦═══════════╗
║ ObjectName ║ StatsName ║ StatsDate ║
╠═══════════════╬═══════════╬═══════════╣
(Dbo.SomeTable, cx, NULL)
(Dbo.SomeTable (i) NULL)
(Dbo.SomeTable, pk, NULL)
(Dbo.SomeTable, d (NULL))
╚═══════════════╩═══════════╩═══════════╝

Vamos reconstruir a tabela inteira e ver se isso atualiza as estatísticas:

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 ║
╠═══════════════╬═══════════╬═════════════════════ ════╣
Para obter mais informações, consulte o artigo: dbo.SomeTable (cx)
(Dbo.SomeTable (i) NULL)
(Dbo.SomeTable, pk, NULL)
(Dbo.SomeTable, d (NULL))
╚═══════════════╩═══════════╩═════════════════════ ════╝

Os resultados mostram que apenas as estatísticas do índice clusterizado foram atualizadas.

Em seguida, realizamos uma UPDATE STATSoperação discreta :

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';

Como você pode ver, acabamos de atualizar as estatísticas na dcoluna:

╔═══════════════╦═══════════╦═════════════════════ ════╗
║ ObjectName ║ StatsName ║ StatsDate ║
╠═══════════════╬═══════════╬═════════════════════ ════╣
Para obter mais informações, consulte o artigo: dbo.SomeTable (cx)
(Dbo.SomeTable (i) NULL)
(Dbo.SomeTable, pk, NULL)
Para obter mais informações, consulte o artigo: dbo.SomeTable.
╚═══════════════╩═══════════╩═════════════════════ ════╝

Agora, atualizaremos as estatísticas em toda a tabela:

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 ║
╠═══════════════╬═══════════╬═════════════════════ ════╣
Para obter mais informações, consulte o site do dbo.SomeTable.
Para obter mais informações, consulte o artigo: dbo.SomeTable (i) (2018-09-17 14: 09: 13.600)
Você pode usar o seguinte comando: dbo.SomeTable (pk) - 2018-09-17 14: 09: 13.603
Para obter mais informações, consulte o artigo: dbo.SomeTable (2018-09-17 14: 09: 13.607)
╚═══════════════╩═══════════╩═════════════════════ ════╝

Como você pode ver, a única maneira de garantir que todas as estatísticas sejam atualizadas é atualizando cada uma manualmente ou com a tabela inteira UPDATE STATISTICS (table);.

MicSim
fonte
@ JeremyWeir - como você pode ver no código de exemplo que acabei de adicionar à pergunta acima, as únicas estatísticas atualizadas são as explicitamente atualizadas por meio de ALTER INDEX ... REBUILDuma UPDATE STATISTICSdeclaração ou de uma . Se a tabela em si for reconstruída, apenas as estatísticas do índice em cluster serão atualizadas. Para sua informação, uma chave primária e um índice em cluster não são necessariamente suportados pelo mesmo objeto de índice.
Max Vernon
5

A página Microsoft Docs para estatísticas do SQL Server afirma :

Operações como reconstruir, desfragmentar ou reorganizar um índice não alteram a distribuição dos dados. Portanto, você não precisa atualizar as estatísticas após executar as operações ALTER INDEX REBUILD, DBCC DBREINDEX, DBCC INDEXDEFRAG ou ALTER INDEX REORGANIZE . O Query Optimizer atualiza as estatísticas quando você reconstrói um índice em uma tabela ou exibe com ALTER INDEX REBUILD ou DBCC DBREINDEX, no entanto, esta atualização de estatísticas é um subproduto da recriação do índice. O Query Optimizer não atualiza as estatísticas após as operações DBCC INDEXDEFRAG ou ALTER INDEX REORGANIZE.

lado B
fonte