Technical Article

INDEX DEFRAG on SQL SERVER 2005

,

The following is a script that I have worked off and on with until I finally decided that I had worked on it enough. Time is prescious when you are a dba ya know. Some of the script is bastardized from others, I choose not to use the dvm because I was more familiar with doing it the other way which is what I had done with SQL Server 2000. Anyway I would enjoy comments. There are also many different ways the script could have been defined, to show my "technical prowess", however, what is here is perfectly functional and easy to understand. In addition, I currently run this script on many transactional systems at increments throughout the day, without error. There is still a lot of customization that can be done for various instances or exceptions. Feel free to make all the changes that you want or need. Hope you all enjoy.

/****** Object: StoredProcedure [dbo].[adminDefragIndexesDaily] Script Date: 06/12/2008 08:31:26 ******/SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[adminDefragIndexesDaily]
AS
--******************************************************************************************--
-- CREATED BY:         SCOTT M. DEXTER                                                    --
-- TITLE:            SR. DATABASE ADMINISTRATOR                                                --
-- MODIFIED BY:             SCOTT M. DEXTER                                                    --
-- MODIFIED ON:        12 JUN 2008
-- CREATED ON:        5 JUN 2008                                                                --
----------------------------------------------------------------------------------------------
-- VARIABLES:        TO DEFRAG INDEXES WITH > 15% FRAGMENTATION.                                --
------------------- THIS VARIABLE CAN BE CHANGED, BUT, THE STORED PROCEDURE WILL            --
------------------- HAVE TO BE ALTERED AS THE VALUES ARE HARD CODED. THESE ARE THE            --
------------------- NORM IN VALIDATING THE PERFORMANCE OF INDEXES ON A TRANSACTIONAL        --
------------------- ENVIRONMENT.                                                            --
--******************************************************************************************--
-- MODIFICATION:    12 JUN 2008                                                                --
-------------------    TO ADD THE BEGIN AND END TIME TO EACH OF THE DEFRAGS SO WE CAN KEEP        --
-------------------    BETTER TRACK OF THE TIME THAT THEY CONSUME. THIS WILL ALLOW US TO        --
------------------- MODIFY THE SCRIPTS ACCORDINGLY AS THEY GET INCREASINGLY LONGER            --
------------------- IN ADDITION, I AM ADDING A LOGGING TABLE TO KEEP THE DATA IN FOR        --
------------------- REFERENCE. ADDED SYSADMIN CONTEXT FOR ENSURING SECURITY AS WELL.        --
--******************************************************************************************--
-- PURPOSE:            THE PROCEDURE IS TO PROVIDE DAILY MAINTENANCE FOR                        --
        ----------- INDEXES ON S01GIS2. THIS IS A CUSTOM PROCEDURE DESIGNED TO                --
        ----------- OPTIMIZE THE EDI PROCESS DUE TO THE AMOUNT OF INSERTS,                    --
        ----------- AND DELETES IN THE TABLES.                                                --
--******************************************************************************************--



BEGIN TRY
SET NOCOUNT ON



DECLARE @SQL VARCHAR(500);
DECLARE @MINDEFRAGID INT; 
DECLARE @MAXDEFRAGID INT;
DECLARE @tableHTML NVARCHAR(MAX) ;
DECLARE @Emailrecipients VARCHAR(100)
DECLARE @EmailSubject VARCHAR(100)
DECLARE @BeginTime smalldatetime
DECLARE @EndTime smalldatetime
DECLARE @ObjectName VARCHAR(128)
DECLARE @DBName VARCHAR(128)
DECLARE @ERROR INT
DECLARE @ERRORMESSAGE VARCHAR(500)

-- SET ENVIRONMENT VARIABLES WHERE THEY CAN BE STATIC.
SET @EmailSubject = 'SUCCESSFULLY DEFRAGMENTED INDEXES ON S01GIS2.'
SET @Emailrecipients = 'YOURHANDIDANDYEMAIL@YOURDOMAIN.COM'
SELECT @DBName = db_name(db_id())

IF IS_SRVROLEMEMBER('sysadmin') <> 1
    BEGIN
            PRINT 'YOU ARE NOT A MEMBER OF THE SYSADMIN FIXED SERVER ROLE. YOU ARE THEREFORE UNABLE TO RUN THIS PROCEDURE.'
            PRINT 'PLEASE CONTACT YOUR HANDI DANDI DBA TO ASSIST WITH RUNNING THIS PROCEDURE.'
            SET @EmailSubject = 'Security violation on ' + @@SERVERNAME + '.'
            SET @ERRORMESSAGE = 'UNAUTHORIZED ACCESS TO ' + OBJECT_NAME(@@PROCID)
    -- SEND MAIL TO DBA IF THERE IS A FAILURE
        EXEC msdb.dbo.sp_send_dbmail 
            @recipients = @Emailrecipients,
            @subject = @EmailSubject,
            @body = @ERRORMESSAGE;
    -- RAISE ERROR IN THE LOG
            RETURN
    END

-- CREATE TABLE TO DISPLAY AND PARSE DBCC SHOWCONTIG RESULTS.
CREATE TABLE #adminShowContigResults
(
objectName varchar(128) not null,
objectID int not null,
indexName varchar(128) not null,
indexID tinyint not null,
level smallint not null,
pages int not null,
rows bigint not null,
minimumRecordSize int not null,
maximumRecordSize int not null,
averageRecordSize decimal(15,3) not null,
forwardedRecords int not null,
extents int not null,
extentSwitches int not null,
averageFreeBytes decimal(15,3) not null,
averagePageDensity decimal(15,3) not null,
scanDensity decimal(15,3) not null,
bestCount int not null,
actualCount int not null,
logicalFragmentation decimal(15,3) not null,
extentFragmentation decimal(15,3) not NULL

)

-- CREATE A TABLE THAT WILL DISPLAY ALL OF THE ITEMS THAT WILL NEED DEFRAGGED.
-- THIS WILL ALSO BE USED IN ANY MESSAGE THAT IS SENT OUT.
CREATE TABLE #defrag
(defragid int identity(1,1) not null,
objectName varchar(128) not null,
pages int not null,
rows bigint not null,
extents int not null,
scanDensity decimal(15,3) not null,
logicalFragmentation decimal(15,3) not null,
newFragmentationLevel decimal(15,3) NULL,
beginTime SMALLDATETIME NULL,
endTime SMALLDATETIME NULL,
)

-- INSERT DATA FROM DBCC SHOWCONTIG TO DISPLAY AND PARSE ALL FRAGMENTATION LEVELS.
INSERT INTO #adminShowContigResults(objectName, objectID, indexName, indexID, level, pages, rows, minimumRecordSize, maximumRecordSize, averageRecordSize, forwardedRecords, extents, extentSwitches, averageFreeBytes, averagePageDensity, scanDensity, bestCount, actualCount, logicalFragmentation, extentFragmentation)
EXEC ('DBCC SHOWCONTIG() WITH TABLERESULTS')

-- DISPLAY ALL OF THE ITEMS THAT WILL NEED DEFRAGGED AND FOR MESSAGING.
INSERT INTO #defrag (objectName, pages, rows, extents, scanDensity, logicalFragmentation)
SELECT objectName, pages, rows, extents, scanDensity, logicalFragmentation
FROM #adminShowContigResults
WHERE rows > 100 
AND extents > 20 
AND averagePageDensity < 85 
AND logicalFragmentation > 15 
AND extentFragmentation > 15
ORDER BY rows

SELECT @MINDEFRAGID = MIN(defragid) FROM #defrag
SELECT @MAXDEFRAGID = MAX(defragid) FROM #defrag

IF @MINDEFRAGID IS NULL
    BEGIN

        SET @tableHTML = N'<STYLE TYPE="text/css">TD{font-family: calibri; font-size: 10pt;}</STYLE>' + 
        N'<b><font face="calibri" size="2">This report represents index(s) which have an average fragmentation > than 15 percent. These index(s) have been targeted for maintenance.</font></b><br><br>' +
        N'<table border="1" cellpadding="2" cellspacing="2" border="1">' +
        N'<tr><th><font face="calibri" size="2">MESSAGE</font></th><th>' +
        CAST ((SELECT td = 'THERE ARE NO TABLES IN THE DATABASE TO DEFRAG' 
        FOR XML PATH('tr'), TYPE 
        ) AS NVARCHAR(MAX) ) +
        N'</table>' ;

        EXEC msdb.dbo.sp_send_dbmail 
            @recipients = @Emailrecipients,
            @subject = @EmailSubject,
            @body = @tableHTML,
            @body_format = 'HTML' ;
    END

ELSE
    BEGIN 

        WHILE (@MINDEFRAGID <= @MAXDEFRAGID)
            BEGIN 
                SELECT @ObjectName = objectName FROM #defrag
                WHERE defragid = @MINDEFRAGID

                SET @SQL = 'dbcc indexdefrag(' + @DBName + ',' + @ObjectName + ') WITH NO_INFOMSGS'

                SELECT @BeginTime = CONVERT(VARCHAR(19), GETDATE(), 120)
                
                EXEC (@SQL)
                
                SELECT @EndTime = CONVERT(VARCHAR(19), GETDATE(), 120)
                
                TRUNCATE TABLE #adminShowContigResults

                SET @SQL = 'DBCC SHOWCONTIG(' + char(39) + 'dbo.'+ @ObjectName + char(39) + ') WITH TABLERESULTS'

                INSERT INTO #adminShowContigResults(objectName, objectID, indexName, indexID, level, pages, rows, minimumRecordSize, maximumRecordSize, averageRecordSize, forwardedRecords, extents, extentSwitches, averageFreeBytes, averagePageDensity, scanDensity, bestCount, actualCount, logicalFragmentation, extentFragmentation)
                    
                EXEC (@SQL)
                

                UPDATE #defrag
                    SET newFragmentationLevel = s.logicalFragmentation,
                        beginTime = @BeginTime,
                        endTime = @EndTime
                FROM #defrag d JOIN #adminShowContigResults s ON
                    d.objectName = s.objectName

                PRINT 'SUCCESSFULLY DEFRAGGED ' + @ObjectName + '.'

                SET @MINDEFRAGID = (SELECT MIN(defragid)
                FROM #defrag
                WHERE defragid > @MINDEFRAGID)
            END

        SET @tableHTML = N'<STYLE TYPE="text/css">TD{font-family: calibri; font-size: 10pt;}</STYLE>' + 
        N'<b><font face="calibri" size="2">This report represents index(s) which have an average fragmentation > than 15 percent. These index(s) have been targeted for maintenance.</font></b><br><br>' +
        N'<table border="1" cellpadding="2" cellspacing="2" border="1">' +
        N'<tr><th><font face="calibri" size="2">Object Name</font></th><th><font face="calibri" size="2">Pages</font></th><th><font face="calibri" size="2">Rows</font></th><th><font face="calibri" size="2">Extents</font></th><th><font face="calibri" size="2">Scan Density</font></th><th><font face="calibri" size="2">Logical Fragmentation</font></th><th><font face="calibri" size="2">New Fragmentation Level</font></th><th><font face="calibri" size="2">BEGIN TIME</font></th><th><font face="calibri" size="2">END TIME</font></th>' +
        CAST ((SELECT td = objectName, '', 
        td = pages, '', 
        td = rows, '',
        td = extents, '',
        td = scanDensity, '',
        td = logicalFragmentation, '', 
        td = newFragmentationLevel, '',
        td = beginTime, '',
        td = endTime 
        FROM #defrag
        FOR XML PATH('tr'), TYPE 
        ) AS NVARCHAR(MAX) ) +
        N'</table>' ;

        EXEC msdb.dbo.sp_send_dbmail 
            @recipients = @Emailrecipients,
            @subject = @EmailSubject,
            @body = @tableHTML,
            @body_format = 'HTML' ;
    END

DROP TABLE #defrag
DROP TABLE #adminShowContigResults

END TRY
BEGIN CATCH
    -- SET VARIABLES FOR ERROR HANDLING
    SET @ERROR = ERROR_NUMBER()
    SET @ERRORMESSAGE = 'MSG ' + CAST(ERROR_NUMBER() AS VARCHAR) + ', ' + ISNULL(ERROR_MESSAGE(),'')
    SET @EmailSubject = 'FAILED TO DEFRAGMENT INDEXES ON S01GIS2.'
    -- SEND MAIL TO DBA IF THERE IS A FAILURE
        EXEC msdb.dbo.sp_send_dbmail 
            @recipients = @Emailrecipients,
            @subject = @EmailSubject,
            @body = @ERRORMESSAGE;
    -- RAISE ERROR IN THE LOG
    RAISERROR(@ERRORMESSAGE,16,1) WITH NOWAIT

END CATCH

Rate

3 (2)

You rated this post out of 5. Change rating

Share

Share

Rate

3 (2)

You rated this post out of 5. Change rating