Search This Blog

INDEXESUSINGSCRIPTINSQLSERVER2005/2008R2

HOW TO DEFRAGMENT INDEXES USING SCRIPT IN SQL SERVER 2005/2008

Background:
When a query is executed on a database, indexes are used to physically locate the data on disk. Due to frequent insert, update and delete statements this data can become fragmented. This is a serious performance issue. To mitigate this schedeule this script in a SQL Agent Job to frequently defragment you indexes.
Steps:
1. Create stored procedure Defragment Indexes in master database. This stored procedure defragments the indexes in your database:
USE master
GO
If (Object_ID('dbo.DefragmentIndexes')>0 )
Begin
    Drop Procedure dbo.DefragmentIndexes
End;
Go
Create Procedure dbo.DefragmentIndexes
  
      @minSize              float           = 50.0 -- MB
    , @minFragmentation     float           = 5.0  -- %
    , @rebuildThreshold     float           = 30.0 -- %  
    , @database             varchar(128)    = Null
    , @onlineRebuild        bit             = 1    
    , @maxDopRestriction    tinyint         = Null
As
/*********************************************************************************
    Author:     Arthur Graus, http://www.sqlserveroptimizer.com
                Michelle Ufford, http://sqlfool.com/
    Purpose:    Defrags all indexes for one or more databases
    Notes:      CAUTION: TRANSACTION LOG SIZE MUST BE MONITORED CLOSELY WHEN DEFRAGMENTING.
    Parameters:
      @minSize              defaulted to 50 MB, will not defrag if size of index
                            is less than that
      @minFragmentation     defaulted to 10%, will not defrag if fragmentation
                            is less than that
       
      @rebuildThreshold     defaulted to 30% as recommended by Microsoft in BOL;
                            greater than 30% will result in rebuild instead
      @database             Optional, specify specific database name to defrag;
                            If not specified, all non-system databases will
                            be defragged.
      @onlineRebuild        1 = online rebuild;
                            0 = offline rebuild
      @maxDopRestriction    Option to specify a processor limit for index rebuilds
      @defragDelay          time to wait between defrag commands; gives the
                            server a little time to catch up
    Sample:
    Exec dbo.DefragmentIndexes
          @minSize              = 1
        , @minFragmentation     = 10
        , @database             = 'AdventureWorks'
*********************************************************************************/                                                             
Set NoCount On;
Set XACT_Abort On;
Set Quoted_Identifier On;
Begin
    /* Declare our variables */
    Declare   @objectID             int
            , @databaseID           int
            , @databaseName         nvarchar(128)
            , @indexID              int
            , @partitionCount       bigint
            , @schemaName           nvarchar(128)
            , @objectName           nvarchar(128)
            , @indexName            nvarchar(128)
            , @partitionNumber      smallint
            , @partitions           smallint
            , @fragmentation        float
            , @pageCount            int
            , @sqlCommand           nvarchar(4000)
            , @rebuildCommand       nvarchar(200)
            , @dateTimeStart        datetime
            , @dateTimeEnd          datetime
            , @containsLOB          bit
            , @editionCheck         bit
            , @debugMessage         varchar(128)
            , @updateSQL            nvarchar(4000)
            , @partitionSQL         nvarchar(4000)
            , @partitionSQL_Param   nvarchar(1000)
            , @LOB_SQL              nvarchar(4000)
            , @LOB_SQL_Param        nvarchar(1000);
    /* Create our temporary tables */
    Create Table #indexes
    (
          databaseID        int
        , databaseName      nvarchar(128)
        , objectID          int
        , indexID           int
        , partitionNumber   smallint
        , fragmentation     float
        , page_count        int
        , defragStatus      bit
        , schemaName        nvarchar(128)   Null
        , objectName        nvarchar(128)   Null
        , indexName         nvarchar(128)   Null
    );
    Create Table #databases
    (
          databaseID        int
        , databaseName      varchar(128)
    );
    Create Table #processor
    (
          [index]           int
        , Name              varchar(128)
        , Internal_Value    int
        , Character_Value   int
    );
    Insert Into #processor
    Execute xp_msver 'ProcessorCount';
    If @maxDopRestriction Is Not Null And @maxDopRestriction > (Select Internal_Value From #processor)
        Select @maxDopRestriction = Internal_Value
        From #processor;
    /* Check our server version; 3 = Enterprise, Enterprise Evaluation or Developer */
    If ((Select ServerProperty('EngineEdition')) = 3 )
        Set @editionCheck = 1 -- supports online rebuilds
    Else
        Set @editionCheck = 0; -- does not support online rebuilds
    Insert Into #databases
    Select database_id
        , name
    From sys.databases
    Where name = IsNull(@database, name)
        And database_id > 4 -- exclude system databases
        And [state] = 0; -- state must be ONLINE
    /* Loop through our list of databases */
    While (Select Count(*) From #databases) > 0
    Begin
        Select Top 1 @databaseID = databaseID
        From #databases;
       /* Determine which indexes to defrag*/
        Insert Into #indexes
        Select
              database_id As databaseID
            , QuoteName(DB_Name(database_id)) As 'databaseName'
            , [object_id] As objectID
            , index_id As indexID
            , partition_number As partitionNumber
            , avg_fragmentation_in_percent As fragmentation
            , page_count
            , 0 As 'defragStatus' /* 0 = unprocessed, 1 = processed */
            , Null As 'schemaName'
            , Null As 'objectName'
            , Null As 'indexName'
             
        From sys.dm_db_index_physical_stats (@databaseID, Null, Null , Null, N'Limited')
        Where avg_fragmentation_in_percent >= @minFragmentation
            And index_id > 0 -- ignore heaps
            And page_count > @minSize * 1024 / 8  -- ignore small objects
        Option (MaxDop 1);
        Delete From #databases
        Where databaseID = @databaseID;
    End
  
    Create Clustered Index CIX_temp_indexDefragList
        On #indexes(databaseID, objectID, indexID, partitionNumber);
    /* Begin our loop for defragging */
    While (Select Count(*) From #indexes Where defragStatus = 0) > 0
    Begin
        /* Start with the most fragmented index first to defrag */
        Select Top 1
              @objectID         = objectID
            , @indexID          = indexID
            , @databaseID       = databaseID
            , @databaseName     = databaseName
            , @fragmentation    = fragmentation
            , @partitionNumber  = partitionNumber
            , @pageCount        = page_count
        From #indexes
        Where defragStatus = 0
        Order By fragmentation Desc;
        /* Look up index information */
        Select @updateSQL = N'Update idl
            Set schemaName = QuoteName(s.name)
                , objectName = QuoteName(o.name)
                , indexName = QuoteName(i.name)
            From #indexes As idl
            Inner Join ' + @databaseName + '.sys.objects As o
                On idl.objectID = o.object_id
            Inner Join ' + @databaseName + '.sys.indexes As i
                On o.object_id = i.object_id
            Inner Join ' + @databaseName + '.sys.schemas As s
                On o.schema_id = s.schema_id
            Where o.object_id = ' + Cast(@objectID As varchar(10)) + '
                And i.index_id = ' + Cast(@indexID As varchar(10)) + '
                And i.type > 0
                And idl.databaseID = ' + Cast(@databaseID As varchar(10));
        Execute sp_executeSQL @updateSQL;
        Select @objectName  = objectName
            , @schemaName   = schemaName
            , @indexName    = indexName
        From #indexes
        Where objectID = @objectID
            And indexID = @indexID
            And databaseID = @databaseID;
         
        /* Determine if the index is partitioned */
        Select @partitionSQL = 'Select @partitionCount_OUT = Count(*)
                                    From ' + @databaseName + '.sys.partitions
                                    Where object_id = ' + Cast(@objectID As varchar(10)) + '
                                        And index_id = ' + Cast(@indexID As varchar(10)) + ';'
            , @partitionSQL_Param = '@partitionCount_OUT int OutPut';
        Execute sp_executeSQL @partitionSQL, @partitionSQL_Param, @partitionCount_OUT = @partitionCount OutPut;
         
        /* Determine if the table contains LOBs */
        Select @LOB_SQL = ' Select Top 1 @containsLOB_OUT = column_id
                            From ' + @databaseName + '.sys.columns With (NoLock)
                            Where [object_id] = ' + Cast(@objectID As varchar(10)) + '
                                And (system_type_id In (34, 35, 99)
                                        Or max_length = -1);'
                            /*  system_type_id --> 34 = image, 35 = text, 99 = ntext
                                max_length = -1 --> varbinary(max), varchar(max), nvarchar(max), xml */
                , @LOB_SQL_Param = '@containsLOB_OUT int OutPut';
        Execute sp_executeSQL @LOB_SQL, @LOB_SQL_Param, @containsLOB_OUT = @containsLOB OutPut;
         
        If @fragmentation < @rebuildThreshold Or @containsLOB = 1 Or @partitionCount > 1
        Begin
         
            Set @sqlCommand = N'Alter Index ' + @indexName + N' On ' + @databaseName + N'.'
                                + @schemaName + N'.' + @objectName + N' ReOrganize';
            /* If our index is partitioned, we should always reorganize */
            If @partitionCount > 1
                Set @sqlCommand = @sqlCommand + N' Partition = '
                                + Cast(@partitionNumber As nvarchar(10));
        End;
        /* If the index is heavily fragmented and doesn't contain any partitions or LOB's, rebuild it */
        If @fragmentation >= @rebuildThreshold And IsNull(@containsLOB, 0) != 1 And @partitionCount <= 1
        Begin
            /* Set online rebuild options; requires Enterprise Edition */
            If @onlineRebuild = 1 And @editionCheck = 1
                Set @rebuildCommand = N' Rebuild With (Online = On';
            Else
                Set @rebuildCommand = N' Rebuild With (Online = Off';
            /* Set processor restriction options; requires Enterprise Edition */
            If @maxDopRestriction Is Not Null And @editionCheck = 1
                Set @rebuildCommand = @rebuildCommand + N', MaxDop = ' + Cast(@maxDopRestriction As varchar(2)) + N')';
            Else
                Set @rebuildCommand = @rebuildCommand + N')';
         
            Set @sqlCommand = N'Alter Index ' + @indexName + N' On ' + @databaseName + N'.'
                            + @schemaName + N'.' + @objectName + @rebuildCommand;
        End;
        /* Executing the SQL?  */
         
        Execute sp_executeSQL @sqlCommand;
        WaitFor Delay '00:00:05';
     
        Update #indexes
        Set defragStatus = 1
        Where databaseID       = @databaseID
          And objectID         = @objectID
          And indexID          = @indexID
          And partitionNumber  = @partitionNumber;
    End
    Drop Table #indexes;
    Drop Table #databases;
    Drop Table #processor;
    Set NoCount Off;
    Return 0
End
Go
    Exec dbo.DefragmentIndexes
          @minSize              = 0.01
        , @minFragmentation     = 5
        , @database             = 'AdventureWorks2008'