Script para reorganizar/recriar índices no Microsoft SQL Server

porPaulo Henrique Corrêa Cardoso

Script para reorganizar/recriar índices no Microsoft SQL Server

Muitas vezes sentimos uma performance terrível em nosso banco de dados.

Um dos fatores que pode fazer isso acontecer é a defasagem dos índices das tabelas de nosso banco.

Isso acontece devido a  grande quantidade de manipulações dos dados, principalmente com os comandos Insert e Delete.

Para isso podemos criar um script para reorganizar, e dependendo dos casos recriar os índices do nosso SQL Server…
Essas ações melhoram o desempenho do banco.

Vou explicar os passos para montar esse Script que vai te ajudar muito.

Primeiramente, vamos criar algumas variáveis de controle que serão utilizadas no momento da leitura dos índices…

DECLARE @tableName nvarchar(500) 
DECLARE @indexName nvarchar(500) 
DECLARE @percentFragment decimal(11,2)
DECLARE @page_count int

 

Na sequência vamos definir nosso Cursor, que servirá para armazenar a lista de índices que iremos realizar a manutenção.

Dentro da criação do cursor, escrevemos um comando Select que vai cruzar dados entre a tabela de status dos índices (sys.dm_db_index_physical_stats), a tebela de tabelas(sys.tables), a tabela de Schemas(sys.schemas) e a tabela de Índices(sys.indexes).

Definimos um parâmetro de busca de fragmentação onde o % de fragmentação for maior que 5% e a quantidade de páginas do índice for maior que 10.

Com isso começamos a pegar os índices que já começam a apresentar lentidão nas consultas…

DECLARE FragmentedTableList cursor for 
SELECT  dbtables.[name] AS 'Table',
    dbindexes.[name] AS 'Index',
    indexstats.avg_fragmentation_in_percent,
    indexstats.page_count
FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL, NULL, NULL) AS indexstats
INNER JOIN sys.tables dbtables 
  ON dbtables.[object_id] = indexstats.[object_id]
INNER JOIN sys.schemas dbschemas 
  ON dbtables.[schema_id] = dbschemas.[schema_id]
INNER JOIN sys.indexes dbindexes 
  ON dbindexes.[object_id] = indexstats.[object_id]
  AND indexstats.index_id = dbindexes.index_id
  AND dbindexes.[name] IS NOT NULL
WHERE indexstats.database_id = DB_ID()
    AND indexstats.avg_fragmentation_in_percent > 05
    AND indexstats.page_count  > 10 
ORDER BY indexstats.page_count DESC, indexstats.avg_fragmentation_in_percent DESC
 
OPEN FragmentedTableList

 

Por último, vamos abri o cursor, fazer a leitura dos índices e executar as devidas manutenção:

Quando o percentual de fragmentação estiver entre 5% e 30%, vamos realizar o reorganize do índice, ou seja, vamos reorganizar este índice.

Agora, se o percentual for maior que 30% vamos realizar o rebuild do índice, ou seja, realizar a reconstrução do mesmo, pois a defasagem do mesmo já esta bem critica.

FETCH NEXT FROM FragmentedTableList  
INTO @tableName, @indexName, @percentFragment, @page_count
 
WHILE @@FETCH_STATUS = 0 
  BEGIN 
    PRINT 'Processando ' + @indexName + ' na tabela ' + @tableName + ' com ' + CAST(@percentFragment AS NVARCHAR(50)) + '% fragmentado' 
       
    IF(@percentFragment BETWEEN 05 AND 30) 
      BEGIN 
        
        EXEC( 'ALTER INDEX ' +  @indexName + ' ON ' + @tableName + ' REORGANIZE;') 
        PRINT 'Concluindo a reorganização do índice ' + @indexName + ' da tabela ' + @tableName 
      END 
    ELSE IF (@percentFragment > 30)
      BEGIN 
        EXEC( 'ALTER INDEX ' +  @indexName + ' ON ' + @tableName + ' REBUILD; ') 
        PRINT 'Concluindo a recriação do índice ' + @indexName + 'da tabela ' + @tableName 
    END  
    FETCH NEXT FROM FragmentedTableList  
    INTO @tableName, @indexName, @percentFragment,@page_count
  END 
CLOSE FragmentedTableList 
DEALLOCATE FragmentedTableList

 

Agora vejam o exemplo completo do nosso script:

DECLARE @tableName nvarchar(500) 
DECLARE @indexName nvarchar(500) 
DECLARE @percentFragment decimal(11,2) 
DECLARE @page_count int
 
DECLARE FragmentedTableList cursor for 
SELECT  dbtables.[name] AS 'Table',
    dbindexes.[name] AS 'Index',
    indexstats.avg_fragmentation_in_percent,
    indexstats.page_count
FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL, NULL, NULL) AS indexstats
INNER JOIN sys.tables dbtables 
  ON dbtables.[object_id] = indexstats.[object_id]
INNER JOIN sys.schemas dbschemas 
  ON dbtables.[schema_id] = dbschemas.[schema_id]
INNER JOIN sys.indexes dbindexes 
  ON dbindexes.[object_id] = indexstats.[object_id]
  AND indexstats.index_id = dbindexes.index_id
  AND dbindexes.[name] IS NOT NULL
WHERE indexstats.database_id = DB_ID()
    AND indexstats.avg_fragmentation_in_percent > 05
    AND indexstats.page_count  > 10 
ORDER BY indexstats.page_count DESC, indexstats.avg_fragmentation_in_percent DESC
 
OPEN FragmentedTableList 
FETCH NEXT FROM FragmentedTableList  
INTO @tableName, @indexName, @percentFragment, @page_count
 
WHILE @@FETCH_STATUS = 0 
  BEGIN 
    PRINT 'Processando ' + @indexName + ' na tabela ' + @tableName + ' com ' + CAST(@percentFragment AS NVARCHAR(50)) + '% fragmentado' 
       
    IF(@percentFragment BETWEEN 05 AND 30) 
      BEGIN 
        
        EXEC( 'ALTER INDEX ' +  @indexName + ' ON ' + @tableName + ' REORGANIZE;') 
        PRINT 'Concluindo a reorganização do índice ' + @indexName + ' da tabela ' + @tableName 
      END 
    ELSE IF (@percentFragment > 30)
      BEGIN 
        EXEC( 'ALTER INDEX ' +  @indexName + ' ON ' + @tableName + ' REBUILD; ') 
        PRINT 'Concluindo a recriação do índice ' + @indexName + 'da tabela ' + @tableName 
    END  
    FETCH NEXT FROM FragmentedTableList  
    INTO @tableName, @indexName, @percentFragment,@page_count
  END 
CLOSE FragmentedTableList 
DEALLOCATE FragmentedTableList

 

Agora o mesmo pode ser executado periodicamente, ajudando a melhorar a performance do banco de dados.

Espero que gostem!!!

Até a próxima !!! 

Sobre o Autor

Paulo Henrique Corrêa Cardoso administrator

Analista de sistemas, formado pela Faculdade de Informática e Administração Paulista . Com mais de 12 anos de experiência em SQL e diversas linguagens de programação. Administrador e desenvolvedor de ERP TOTVS Protheus.

8 Comentários até agora

jpPostado em10:11 pm - jan 14, 2021

Mano, vc não testou, ta com pau o script.

    Paulo Henrique Corrêa CardosoPostado em10:12 am - jan 15, 2021

    Olá JP, tudo bem ?
    Obrigado pelo seu comentário.
    No momento em que fui adicionar o script ao artigo, acabei removendo a declaração da variável @page_count.
    Já realizei a correção da mesma.
    Obrigado pela sua contribuição.

joshPostado em3:10 pm - dez 8, 2021

amigo como executo esse script em todos os bancos de dados de um servidor, preciso que faça isso em todas as tabelas de todos os bancos de dados de único servidor

    Paulo Henrique Corrêa CardosoPostado em3:55 pm - dez 10, 2021

    Olá Josh, tudo bem ?
    Acredito que você necessite executar isso regularmente para diversos bancos.
    Neste caso eu te aconselho a utilizar o SQL Server Agent. Nele você pode configurar um JOB de manutenção e adicionar um Step para cada banco de dados que você possui em sua instância, além de configurar o mesmo para executar periodicamente por meio de Schedule.

    Segue o link da documentação da Microsoft sobre a criação de JOB no SQL Server Agent.
    https://docs.microsoft.com/pt-br/sql/ssms/agent/create-a-job?view=sql-server-ver15#SSMSProcedure

    Vou dedicar um tempo, para criar um Post sobre como configurar um JOB de manutenção de índices.

oseas de almeida ribeiroPostado em12:00 pm - jan 3, 2022

Como eu executo essa rotina ?

SauloPostado em12:40 pm - fev 22, 2022

Durante a execução acontece muito o erro com algumas tabelas específicas:

Msg 1088, Level 16, State 9, Line 1
Não é possível encontrar o objeto “nome_da_tabela” porque ele não existe ou você não tem permissões.

Praticamente o código não executa por não conseguir achar determinadas tabelas. Entretanto as tabelas existem, porém o REBUILD só consigo executar a cada tabela com o comando:

ALTER INDEX ALL ON dbo.nome_da_tabela
REBUILD;

Ou seja, apenas com a inserção do nome do BD (shema) ao nome da tabela no comando de RECRIAR índices para cada tabela.

SchubertPostado em9:22 am - ago 24, 2022

Excelente artigo, nos índex de um dos bancos que fiz o teste, ele possuía o char “-” no meio do nome, o que quebrava o exec, então adicionei aspas duplas no nome do índice antes de concatenar a variável, ficando da seguinte forma os execs:

IF(@percentFragment BETWEEN 05 AND 30)
BEGIN

EXEC( ‘ALTER INDEX “‘ + @indexName + ‘” ON ‘ + @tableName + ‘ REORGANIZE;’)
PRINT ‘Concluindo a reorganização do índice ‘ + @indexName + ‘ da tabela ‘ + @tableName
END
ELSE IF (@percentFragment > 30)
BEGIN
EXEC( ‘ALTER INDEX “‘ + @indexName + ‘” ON ‘ + @tableName + ‘ REBUILD; ‘)
PRINT ‘Concluindo a recriação do índice ‘ + @indexName + ‘da tabela ‘ + @tableName
END

Gabriel LopesPostado em4:36 pm - out 19, 2022

Fiz algumas alterações pois meu banco continha índices em outros schemas e estava dando erro ao tentar reindexar esses índices, pois o comando não incluía o schema.

DECLARE @tableName nvarchar(500)
DECLARE @indexName nvarchar(500)
DECLARE @percentFragment decimal(11,2)
DECLARE @page_count int
DECLARE @schemaName nvarchar(500)

DECLARE FragmentedTableList cursor for
SELECT dbtables.[name] AS ‘Table’,
dbindexes.[name] AS ‘Index’,
indexstats.avg_fragmentation_in_percent,
indexstats.page_count,
dbschemas.[name] as ‘Schema’

FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL, NULL, NULL) AS indexstats
INNER JOIN sys.tables dbtables
ON dbtables.[object_id] = indexstats.[object_id]
INNER JOIN sys.schemas dbschemas
ON dbtables.[schema_id] = dbschemas.[schema_id]
INNER JOIN sys.indexes dbindexes
ON dbindexes.[object_id] = indexstats.[object_id]
AND indexstats.index_id = dbindexes.index_id
AND dbindexes.[name] IS NOT NULL
WHERE indexstats.database_id = DB_ID()
AND indexstats.avg_fragmentation_in_percent > 05
AND indexstats.page_count > 10
–AND dbschemas.[name] = ‘dbo’
ORDER BY indexstats.page_count DESC, indexstats.avg_fragmentation_in_percent DESC

OPEN FragmentedTableList
FETCH NEXT FROM FragmentedTableList
INTO @tableName, @indexName, @percentFragment, @page_count, @schemaName

WHILE @@FETCH_STATUS = 0
BEGIN
PRINT ‘Processando ‘ + @indexName + ‘ na tabela ‘ + @tableName + ‘ com ‘ + CAST(@percentFragment AS NVARCHAR(50)) + ‘% fragmentado’

IF(@percentFragment BETWEEN 05 AND 30)
BEGIN

EXEC( ‘ALTER INDEX “‘ + @indexName + ‘” ON [‘ +@schemaName+ ‘].’ + @tableName + ‘ REORGANIZE;’)
PRINT ‘Concluindo a reorganização do índice ‘ + @indexName + ‘ da tabela ‘ + @tableName
END
ELSE IF (@percentFragment > 30)
BEGIN
EXEC( ‘ALTER INDEX “‘ + @indexName + ‘” ON [‘ +@schemaName+ ‘].’ + @tableName + ‘ REBUILD;’)
PRINT ‘Concluindo a recriação do índice ‘ + @indexName + ‘da tabela ‘ + @tableName
END
FETCH NEXT FROM FragmentedTableList
INTO @tableName, @indexName, @percentFragment,@page_count,@schemaName
END
CLOSE FragmentedTableList
DEALLOCATE FragmentedTableList

Deixe uma resposta

%d blogueiros gostam disto: