SQL Server Index Fragmentation Script

Just sharing a cool script I stumbled across while searching for SQL Server index fragmentation scripts (Omer van Kloeten’s Blog). I’ve cleaned it up a bit and turned it into a Stored Procedure so that I can execute it as a SQL Server Agent Job (Not sure if this is a good idea or not so feel free to leave comments).

So why do we need to worry about Index Fragmentation?

*The SQL Server Database Engine automatically maintains indexes whenever insert, update, or delete operations are made to the underlying data. Over time these modifications can cause the information in the index to become scattered in the database (fragmented). Fragmentation exists when indexes have pages in which the logical ordering, based on the key value, does not match the physical ordering inside the data file. Heavily fragmented indexes can degrade query performance and cause your application to respond slowly. For more information, see this Microsoft Web site.

Please Note: Defragmentation does not yield performance gains in every case. Every scenario is different. Similarly, determining when you should run the defragmentation statements requires analysis.

*Reorganizing and Rebuilding Indexes (http://technet.microsoft.com/en-us/library/ms189858.aspx)

MSDN Gallery: http://code.msdn.microsoft.com/SQL-Server-Index-6c12e7d4

CREATE PROC usp_FixIndexFragmentation
(
    /* Use online rebuild if detected  edition is Enterprise, Developer or Evaluation.*/
    @Online BIT = 1,

    /* Log recommendations Only */    
    @LogOnly BIT = 1,

    /* Specifies a percentage that indicates how full the Database Engine should 
    make the leaf level of each index page during index creation or alteration. 
    Fillfactor must be an integer value from 1 to 100. The default is 0. */
    @FillFactor TINYINT = 50,

   /* Specifies the scan level that is used to obtain statistics. Valid inputs are 
    DEFAULT, NULL, LIMITED, SAMPLED, or DETAILED. */
    @Index_physical_stats_mode VARCHAR(20) = 'Limited',


    /* Total number of index or data pages. */
    @Index_physical_stats_page_count BIGINT = 500,


    /* Logical fragmentation for indexes, or extent fragmentation for heaps in the 
    IN_ROW_DATA allocation unit. The value is measured as a percentage and 
    takes into account multiple files */
    @Avg_fragmentation_percent TINYINT = 40
)
AS
BEGIN
    SET NOCOUNT ON

    DEClARE @Rows BIGINT = 0
    DEClARE @Counter BIGINT = 1
    DECLARE @TSQLStatement NVARCHAR(MAX)

    IF OBJECT_ID('tempdb..#TmpTable') IS NOT NULL 
        DROP TABLE #TmpTable 
          
    CREATE TABLE #TmpTable 
    (
        RowNo BIGINT IDENTITY(1,1), TSQLStatement VARCHAR(MAX)
    )
          
    IF NOT EXISTS 
    (
        SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[LogTable]') 
        AND type in (N'U')
    )
    BEGIN
        CREATE TABLE [dbo].[LogTable]
        (
            [TSQLStatement] [varchar](max) NULL,
            [FRAGMENTATION] [float] NULL,
            [Executed] [datetime] NULL,
            [ExecutedBy] [varchar](100) NULL
        ) ON [PRIMARY]
    END
                    
    INSERT INTO LogTable
    OUTPUT INSERTED.TSQLStatement INTO #TmpTable
    SELECT    'ALTER INDEX [' + ix.name + '] ON [' + s.name + '].[' + t.name + '] ' +
    CASE
        WHEN ps.avg_fragmentation_in_percent > @Avg_fragmentation_percent THEN
        CASE
            WHEN @Online = 1 AND 
                (
                    CAST(SERVERPROPERTY('Edition') AS VARCHAR) LIKE 'Enterprise%'
                    OR
                    CAST(SERVERPROPERTY('Edition') AS VARCHAR) LIKE 'Developer%'

                    OR
                    CAST(SERVERPROPERTY('Edition') AS VARCHAR) LIKE 'Evaluation%'
                )
                THEN 'REBUILD WITH (ONLINE = ON, FILLFACTOR = ' 
                    + CAST(@FillFactor AS VARCHAR(10)) + ') '
            ELSE 'REBUILD WITH (FILLFACTOR = ' 
                + CAST(@FillFactor AS VARCHAR(10)) + ') '
        END
        ELSE 'REORGANIZE '
    END +
    CASE
        WHEN pc.partition_count > 1 
            THEN ' PARTITION = ' + CAST(ps.partition_number AS NVARCHAR(MAX))
        ELSE ''
    END,
    PS.AVG_FRAGMENTATION_IN_PERCENT, GETDATE(), SUSER_NAME()
    FROM sys.indexes AS ix INNER JOIN sys.tables t
        ON t.object_id = ix.object_id     INNER JOIN sys.schemas s
        ON t.schema_id = s.schema_id INNER JOIN 
        (         
            SELECT object_id, index_id, avg_fragmentation_in_percent, partition_number
            FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL, NULL, 
                @Index_physical_stats_mode) 
            WHERE page_count > @Index_physical_stats_page_count
        ) ps
            ON t.object_id = ps.object_id AND ix.index_id = ps.index_id INNER JOIN 
    (
        SELECT object_id, index_id, COUNT(DISTINCT partition_number) AS partition_count
        FROM sys.partitions
        GROUP BY  object_id, index_id
    ) pc
            ON t.object_id = pc.object_id AND ix.index_id = pc.index_id
    WHERE ps.avg_fragmentation_in_percent > 10 AND ix.name IS NOT NULL
    ORDER BY  ps.avg_fragmentation_in_percent DESC

    SET @Rows = @@ROWCOUNT

    IF @LogOnly = 0
    BEGIN
        WHILE (@Counter <= @Rows)
            BEGIN
            SELECT @TSQLStatement = TSQLStatement
            FROM #TmpTable
            WHERE RowNo = @Counter

            EXECUTE sp_executesql @TSQLStatement

            SET @Counter += 1
            END
    END
    
  SET NOCOUNT OFF
END
Advertisements

About Mr. Wharty's Ramblings
Jeff holds a Master’s Degree in Systems Development (.NET Stream) and a Master’s Degree in Database Design and Management (SQL Server). Jeff also holds MCPD, MCITP, MCDBA, MCSD and MCSE certifications.

One Response to SQL Server Index Fragmentation Script

  1. Martin says:

    I am new to “this.” Since I am coming from a Microsoft SQL Server einrnovment, and to some other it may seem like the two versions should be very similar, and they are, to a certain degree, but they are also very different in some important and basic ways. Good read, thanks!

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: