Há uns dias surgiu uma questão bem pertinente no seio da minha equipa:
“No fim do processo quero fazer rebuild à tabela, isso actualiza todas as estatísticas também?”
Resposta… aquela famosa “resposta do informático”: Depende.
Resposta do outro lado? Depende do quê?
Resposta: De muita coisa.
Bem, nada como vermos com os nosso olhos. Vamos lá mostrar do que depende.
Como base, para os menos versados no assunto, apenas necessitamos de saber que há vários tipos de estatísticas. Criadas para os nossos índices, criadas por nós e, se AUTO_CREATE_STATISTICS estiver ON, criadas automaticamente pelo SQL Server nas colunas que usamos.
Para garantir que temos as mesmas condições que as do meu teste (e já agora seguir as boas práticas recomendadas)
ALTER DATABASE Demo01 SET AUTO_CREATE_STATISTICS, AUTO_UPDATE_STATISTICS ON go
Criamos uma tabelazinha, daquelas que só se criam para este tipo de coisas
create table UpdateStatisticsDemo ( id int identity(1,1) , col1 int not null , col2 int not null , col3 varchar(50) not null , col4 datetime not null ) go
E agora criamos 3 novos objectos, um índice clustered, um nonclustered e uma estatística.
create clustered index cidx on UpdateStatisticsDemo (id) go create nonclustered index ncidx_col1 on UpdateStatisticsDemo(col1) go create statistics Col2Col4 on UpdateStatisticsDemo (Col2, Col4) with fullscan go
Vamos ver agora as estatísticas que temos criadas nesta tabela
select * from sys.stats where OBJECT_NAME(object_id) = 'UpdateStatisticsDemo'
Como esperado, vemos estatísticas sobre o índice clustered, nonclustered e a estatística criada por nós.
Já agora, vamos ver quando é que foram actualizadas?
SELECT [name] , [updated] = STATS_DATE(object_id, stats_id) , stats_id , auto_created , user_created FROM sys.stats WHERE object_id = OBJECT_ID('UpdateStatisticsDemo');
“Nunca” é uma resposta fácil para queijinho. A tabela nem sequer tem dados…
Vamos lá tratar disso.
insert into UpdateStatisticsDemo (col1, col2, col3, col4) select CAST(RAND() * 100000 AS INT) , CAST(RAND() * 500000 AS INT) , cast(NEWID() as varchar(50)) , DATEADD(SECOND, ROUND(((DATEDIFF(SECOND, '2019-01-01 08:22:13', '2019-07-14 12:56:31')-1) * RAND()), 0), '2019-01-01 08:22:13') go 1000
E agora? As estatísticas foram actualizadas?
Sim? Quais? Todas? Alguma?
Nenhuma!
E o que acontece se fizermos uma query com predicado numa onde não temos estatísticas?
select id from UpdateStatisticsDemo where col3 like '%123%'
Foi criada uma estatística nova, aquela _WA_Sys_blablablabla. E essa foi actualizada, porque já que o SQL Server varreu a coluna, mais vale aproveitar…
E se fizermos outra query, desta vez sobre uma coluna já com estatística criada?
select id from UpdateStatisticsDemo where col2 > 50000
Sim, como esperado, já que varreu a coluna, actualizam-se as estatísticas.
Agora vamos à parte do “depende”.
Será que com um REBUILD da tabela todas as estatísticas são actualizadas?
alter table UpdateStatisticsDemo rebuild
Quem é que disse “só a estatística sobre o índice clustered e mais nenhuma”? Seria de esperar, pois o índice clustered, afinal, é a tabela.
Mas vejam como nenhuma outra estatística foi actualizada.
Será que um REBUILD a todos os índices o faz?
alter index all on UpdateStatisticsDemo rebuild
Quem é que respondeu “Só aos índices?” (Esta era mais fácil…)
Sem grandes dúvidas, alter index actualiza estatísticas nas colunas que pertencem aos índices.
Agora vamos lá fazer o que falta…
update statistics UpdateStatisticsDemo
Agora sim, todas as nossas estatísticas foram actualizadas.
Resposta à pergunta “no fim do processo quero fazer rebuild à tabela, isso actualiza todas as estatísticas também?” mantém-se a mesma. Depende.
Depende se a tabela acabou de ser criada. Depende de ter um índice clustered. Depende se tem índices nonclustered. Depende se tem já estatísticas criadas pelo SQL Server em outras colunas. Depende se a tabela tem estatísticas criadas por nós.
Mas pelo menos agora já vimos o que precisamos de fazer nesses casos.