Defragmenting Indexes

The following SQL code is an example of a manual maintenance utility for defragmenting table indexes.


/*
Author:		Matthew Wells (GE) Date Created:	2006/02/21

Description:
=========
This routine checks the level of index fragmentation, then defragments the indexes and then reindexes them.

Change Date	Who	What
===========	====	=====
*/		

DECLARE @IndexList TABLE (	RowId	int IDENTITY PRIMARY KEY,
                                                      ObjectId int,
                                                      IndexId	int)

CREATE TABLE #Indexes (  RowId	           int IDENTITY PRIMARY KEY,
                                    ObjectName			       varchar(128),
                                    ObjectId		int,	
                                    IndexName			        varchar(128),
                                    IndexId			          int,
                                    Level			            int,
                                    Pages			            int,
                                    Rows			             int,
                                    MinimumRecordSize    int,	
                                    MaximumRecordSize    int,	
                                    AverageRecordSize	int,		
                                    ForwardedRecords	int,		
                                    Extents			          int,
                                    ExtentSwitches		int,	
                                    AverageFreeBytes	real,		
                                    AveragePageDensity		real,	
                                    ScanDensity			real,
                                    BestCount			int,
                                    ActualCount			int,
                                    LogicalFragmentation	real,		
                                    ExtentFragmentation		real)	

DECLARE	@ObjectName	varchar(128), 
              @ObjectId	 int,
              @IndexName	varchar(128),
              @IndexId	  int,
              @Debug	    int,
              @Defragment     int,
              @Reindex	 int,
              @Start_Time     datetime,
              @End_Time	datetime,
              @Time	    real,
              @Total_Start_Time	datetime, 
              @Total_End_Time		datetime, 
              @Total_Time			real,
              @Query_Time	             real,
              @Defrag_Time	            real,           
              @Reindex_Time	           real,
              @Rows	                   int,
              @Row	                    int,
              @ScanDensity	            float,
              @LogicalFragmentation	float,
              @DBCCCOMMAND		varchar(25), 
              @DBCCOPTIONS	  varchar(25),
             @SCANDENSITYLIMIT	float,
             @FRAGMENTATIONLIMIT	float

--	Initialization	--

-- Constants
SELECT @DBCCCOMMAND	     = 'DBCC SHOWCONTIG (', 
            @DBCCOPTIONS	= ') WITH TABLERESULTS', 
            @SCANDENSITYLIMIT  = 90.0,
            @FRAGMENTATIONLIMIT = 10.0

-- Parameters
SELECT @Defrag_Time	= 0,
          @Reindex_Time	         = 0, 
          @Total_Start_Time = getdate(),
          @Debug	                   = 1,
          @Defragment	              = 0, -- SET THIS TO 1 TO DEFRAGMENT INDEXES
          @Reindex	                 = 0 -- SET THIS TO 1 TO REBUILD INDEXES

--	Get Fragmented Indexes	--

SELECT @Start_Time = getdate() 
IF @Debug = 1
        BEGIN
        PRINT 'Querying Indexes...' 
        END
INSERT INTO @IndexList (  ObjectId,
SELECT si.id,
            si.IndID 
FROM sysindexes si
JOIN sysobjects so ON    so.name = si.name
                                             AND si.id = so.Parent_obj 
                                             AND (	so.xtype = 'PK'
                                                                    OR so.xtype = 'UQ')
WHERE si.IndID > 0
SELECT @Rows  = @@ROWCOUNT,
                @Row	= 0
WHILE @Row < @Rows 
      BEGIN
      SELECT @Row = @Row + 1
      SELECT @ObjectId = ObjectId,
                         @IndexId = IndexId
 
FROM @IndexList 
WHERE RowId = @Row
INSERT #Indexes (ObjectName,
 
                                           ObjectId, 
                                           IndexName, 
                                           IndexId, 
                                           Level, 
                                           Pages, 
                                           Rows,
                                           MinimumRecordSize, 
                                           MaximumRecordSize, 
                                           AverageRecordSize, 
                                           ForwardedRecords, 
                                           Extents, 
                                           ExtentSwitches, 
                                           AverageFreeBytes, 
                                           AveragePageDensity, 
                                           ScanDensity,
                                           BestCount, 
                                           ActualCount, 
                                           LogicalFragmentation, 
                                           ExtentFragmentation)
EXEC (@DBCCCOMMAND + @ObjectId + ',' + @IndexId + @DBCCOPTIONS) 
END
SELECT @Query_Time = convert(real, datediff(s, @Start_Time, getdate()))/60.0 
IF @Debug = 1
        BEGIN
        SELECT ObjectName,
               IndexName, 
               IndexId, 
               ScanDensity,
               LogicalFragmentation, 
               ExtentFragmentation
FROM #Indexes
WHERE ScanDensity < @SCANDENSITYLIMIT
                OR LogicalFragmentation > @FRAGMENTATIONLIMIT
ORDER BY	 ObjectName ASC, 
                      IndexName ASC
PRINT 'Queried Indexes in ' + ltrim(str(@Query_Time, 25, 2)) + ' min' 
END

--	Defragment Indexes	--

IF @Defragment = 1
BEGIN
SELECT @Row = 0
WHILE @Row < @Rows 
BEGIN
SELECT @Row = @Row + 1

SELECT  @ObjectName	             = ObjectName, 
        @ObjectId		                  = ObjectId,
        @IndexName	                       = IndexName,
        @IndexId	                         = IndexId,
        @ScanDensity		               = ScanDensity, 
        @LogicalFragmentation	      = LogicalFragmentation
FROM #Indexes 
WHERE RowId = @Row
IF	@ScanDensity < @SCANDENSITYLIMIT
        OR @LogicalFragmentation > @FRAGMENTATIONLIMIT 
        BEGIN
        IF @Debug = 1
                BEGIN
                PRINT 'Defragmenting ' + @ObjectName + '.' + @IndexName 
                END
SELECT @Start_Time = getdate() 
IF @Debug = 1
          BEGIN
          DBCC INDEXDEFRAG (0, @ObjectId, @IndexId) 
          END
 ELSE
          BEGIN
          DBCC INDEXDEFRAG (0, @ObjectId, @IndexId) WITH NO_INFOMSGS 
          END
 SELECT @Time = convert(real, datediff(s, @Start_Time, getdate()))/60.0 
 SELECT @Defrag_Time = @Defrag_Time + @Time
IF @Debug = 1
          BEGIN
          PRINT 'Defragmented ' + @ObjectName + '.' + @IndexName + ' in ' + ltrim(str(@Time, 25, 2)) + 

' min'
                        END
                  END
             END
      END

--	Rebuild Indexes	--
IF @Reindex = 1
BEGIN
SELECT @Row = 0

WHILE @Row < @Rows 
      BEGIN
      SELECT @Row = @Row + 1

SELECT @ObjectName	                 = ObjectName, 
             @IndexName		                = IndexName,
             @ScanDensity		   = ScanDensity, 
             @LogicalFragmentation	= LogicalFragmentation
FROM #Indexes 
WHERE RowId = @Row
IF	@ScanDensity < @SCANDENSITYLIMIT
         OR @LogicalFragmentation > @FRAGMENTATIONLIMIT 
         BEGIN
IF @Debug = 1
         BEGIN
         PRINT 'Reindexing ' + @ObjectName + '.' + @IndexName 
         END
SELECT @Start_Time = getdate() 
IF @Debug = 1
          BEGIN
          DBCC DBREINDEX (@ObjectName, @IndexName) 
          END
 ELSE
          BEGIN
          DBCC DBREINDEX (@ObjectName, @IndexName) WITH NO_INFOMSGS 
          END
 
SELECT @Time = convert(real, datediff(s, @Start_Time, getdate()))/60.0 
SELECT @Reindex_Time = @Reindex_Time + @Time
IF @Debug = 1
           BEGIN
           PRINT 'Reindexed ' + @ObjectName + '.' + @IndexName + ' in ' + ltrim(str(@Time, 25, 2)) + '
min'
            END
        END
    END
 END
 
--	End Game	--

SELECT @Total_Time = convert(real, datediff(s, @Total_Start_Time, getdate()))/60.0
IF @Debug = 1
        BEGIN
        PRINT 'Finished!'
        PRINT 'Query Time=' + ltrim(str(@Query_Time, 25, 2)) + ' min' 
        PRINT 'Defrag Time=' + ltrim(str(@Defrag_Time, 25, 2)) + ' min' 
        PRINT 'Reindex Time=' + ltrim(str(@Reindex_Time, 25, 2)) + ' min'
 
END

DROP TABLE #Indexes