Automate INDEX rebuild based on fragmentation results?

后端 未结 4 1888
心在旅途
心在旅途 2021-02-02 00:26

Is it possible to add a maintenance job to check indexes fragmentation. If greater than 50% then rebuild those indexes automatically ?

Index size can vary from 100MB to

相关标签:
4条回答
  • 2021-02-02 00:29

    You can use sys.dm_db_index_physical_stats to get information about your index fragmentation (see the avg_fragmentation_in_percent column). Then you can do an alter index with the rebuild clause whenever your threshold is reached.

    0 讨论(0)
  • 2021-02-02 00:33

    In case, you were thinking of avoiding to create any temp tables and parsing the string to create a list of SQL strings. Here is an efficient way of accomplishing it:

    USE databasename
    
    GO
    
    DECLARE @Queryresult NVARCHAR(4000)
    
    SET @Queryresult=''
    
    SELECT
    
      @Queryresult=@Queryresult + 'ALTER INDEX ' + QUOTENAME(i.name) + ' ON '
      + QUOTENAME('dbo') + '.'
      + QUOTENAME(OBJECT_NAME(i.OBJECT_ID)) + ' REBUILD;'
    
     FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'SAMPLED') ss
    
      INNER JOIN sys.indexes i ON i.OBJECT_ID = ss.OBJECT_ID AND i.index_id = ss.index_id
    
      INNER JOIN sys.objects o ON ss.object_id = o.object_id
    
    WHERE ss.avg_fragmentation_in_percent > 50
    
    AND ss.record_count > 0 
    
    AND o.is_ms_shipped = 0 --Excludes any objects created as a part of SQL Server installation
    
    AND ss.index_id > 0     --Excludes heap indexes
    
    EXEC sp_executesql @Queryresult
    
    0 讨论(0)
  • 2021-02-02 00:43

    I use this script . Please note I would advise you reading up about the dmv I am using here they are a hidden gem in SQL2005+.

    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
    CREATE TABLE #FragmentedIndexes
    (
     DatabaseName SYSNAME
     , SchemaName SYSNAME
     , TableName SYSNAME
     , IndexName SYSNAME
     , [Fragmentation%] FLOAT
    )
    
    INSERT INTO #FragmentedIndexes
    SELECT
     DB_NAME(DB_ID()) AS DatabaseName
     , ss.name AS SchemaName
     , OBJECT_NAME (s.object_id) AS TableName
     , i.name AS IndexName
     , s.avg_fragmentation_in_percent AS [Fragmentation%]
    FROM sys.dm_db_index_physical_stats(db_id(),NULL, NULL, NULL, 'SAMPLED') s
    INNER JOIN sys.indexes i ON s.[object_id] = i.[object_id]
    AND s.index_id = i.index_id
    INNER JOIN sys.objects o ON s.object_id = o.object_id
    INNER JOIN sys.schemas ss ON ss.[schema_id] = o.[schema_id]
    WHERE s.database_id = DB_ID()
    AND i.index_id != 0
    AND s.record_count > 0
    AND o.is_ms_shipped = 0
    DECLARE @RebuildIndexesSQL NVARCHAR(MAX)
    SET @RebuildIndexesSQL = ''
    SELECT
     @RebuildIndexesSQL = @RebuildIndexesSQL +
    CASE
     WHEN [Fragmentation%] > 30
       THEN CHAR(10) + 'ALTER INDEX ' + QUOTENAME(IndexName) + ' ON '
          + QUOTENAME(SchemaName) + '.'
          + QUOTENAME(TableName) + ' REBUILD;'
     WHEN [Fragmentation%] > 10
        THEN CHAR(10) + 'ALTER INDEX ' + QUOTENAME(IndexName) + ' ON '
        + QUOTENAME(SchemaName) + '.'
        + QUOTENAME(TableName) + ' REORGANIZE;'
    END
    FROM #FragmentedIndexes
    WHERE [Fragmentation%] > 10
    DECLARE @StartOffset INT
    DECLARE @Length INT
    SET @StartOffset = 0
    SET @Length = 4000
    WHILE (@StartOffset < LEN(@RebuildIndexesSQL))
    BEGIN
     PRINT SUBSTRING(@RebuildIndexesSQL, @StartOffset, @Length)
     SET @StartOffset = @StartOffset + @Length
    END
    PRINT SUBSTRING(@RebuildIndexesSQL, @StartOffset, @Length)
    EXECUTE sp_executesql @RebuildIndexesSQL
    DROP TABLE #FragmentedIndexes
    

    Also keep in mind that this script can run a while and block access to your tables. Unless you have Enterprise editions SQL can LOCK the table when rebuilding the index. This will block all queries to that table using the index till the index defrag is finished. Thus it is not advised to run index rebuild during operational hours only during maintenance windows. If you are running enterprise edition you can use the ONLINE=ON option to defrag indexes online. This will use more space but your tables wont be blocked/locked during the defrag operation.

    Shout if you need more information.

    UPDATED:

    If you are running this query on a smaller database you can probably use the 'DETAILED' parameter in the call to sys.dm_db_index_physical_stats. This is probably a more detailed examination of the indexes. The discussion in the comments will also point out that on much larger tables it is probably worth doing a SAMPLED scan as this will help reduce the time needed to do the index scan.

    0 讨论(0)
  • 2021-02-02 00:53

    yes, you can.

    You can get the fragmented indexes using this query:

    SELECT OBJECT_NAME(i.OBJECT_ID) AS TableName,
    i.name AS IndexName,
    indexstats.avg_fragmentation_in_percent
    FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'DETAILED') indexstats
    INNER JOIN sys.indexes i ON i.OBJECT_ID = indexstats.OBJECT_ID
    AND i.index_id = indexstats.index_id
    WHERE indexstats.avg_fragmentation_in_percent > 20
    

    and based on the result just build a command to recreate them

    I would wrap everything on a Stored Procedure and call it from a SQL Server Job

    FYI, 50% is a very big fragmentation. I would go with less.

    0 讨论(0)
提交回复
热议问题