Technical Article

Index Defragmentation

,

Index Defragmentation is one of the most important DBA tasks. This will significantly improve query performance. When you perform any DML operation (INSERT, UPDATE, or DELETE statements) table fragmentation can occur. If you use predefined maintenance plan it will take much server resource and time. Here is a custom stored procedure.

If you want to determine the level of fragmentation, you can use the SYS.DM_DB_INDEX_PHYSICAL_STATS statement. The SYS.DM_DB_INDEX_PHYSICAL_STATS DMV displays fragmentation information for the data and indexes of the specified object.

Here I use 4 conditions :

1. Fragmentation >=30 AND PAGES>1000 then rebuild

2. Fragmentation between 15 to 29 AND PAGES>1000 then reorganize & update statistics

3. If the page level locking is disabled then rebuild

4. If the above conditions are false and statistics are outdated more than 10 days then update the statistics

Before you run the procedure create the tables provided for history propose

Click the following URL Index Architecture By Gail Shaw-->http://www.sqlservercentral.com/articles/Indexing/68439/

Note : This Index Defragmentation script only works for SQL server 2005 and sql server 2008.

You can also get defragmentation script for SQL server 2000 here.

http://www.sqlserverblogforum.com/2011/03/index-defragmentation-script-for-sql-server-2000/

-- For SQL-2005/2008
USE MSDB;
go
CREATE TABLE [dbo].[dba_defrag_maintenance_history]
(
[db_name] [SYSNAME] NOT NULL,
[table_name] [SYSNAME] NOT NULL,
[index_name] [SYSNAME] NOT NULL,
[frag] [FLOAT] NULL,
[page] [INT] NULL,
[action_taken] [VARCHAR](35) NULL,
[last_stats_date] [datetime] NULL,
[date] [DATETIME] NULL DEFAULT (GETDATE())
)
go
--Archive the data's in master DB
USE MASTER;
go
CREATE TABLE [dbo].[dba_defrag_maintenance_history]
(
[db_name] [SYSNAME] NOT NULL,
[table_name] [SYSNAME] NOT NULL,
[index_name] [SYSNAME] NOT NULL,
[frag] [FLOAT] NULL,
[page] [INT] NULL,
[action_taken] [VARCHAR](35) NULL,
[last_stats_date] [datetime] NULL,
[date] [DATETIME] NULL DEFAULT (GETDATE())
)
go

USE [msdb]
GO

/****** Object:  StoredProcedure [dbo].[indexdefragmentation]    Script Date: 09/07/2012 05:18:52 ******/SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

--drop proc [indexdefragmentation]
CREATE PROC [dbo].[indexdefragmentation]@p_dbname SYSNAME
/*
Summary:     Remove the Index Fragmentation to improve the query performance
Contact:        Muthukkumaran Kaliyamoorhty SQL DBA
Description:   This Sproc will take the fragmentation details and do four kinds of work.
 
1. Check the fragmentation greater than 30% and pages greater than 1000 then rebuild
2. Check the fragmentation between 5% to 30% and pages greater than 1000 then reorganize & update stats
3. Check the fragmentation between 5% to 30% and pages greater than 1000 and page level lock disabled then rebuild
4. Update the statistics if the above conditions are false
/*statistics will be updated if its gets 10 days older */ChangeLog:
Date              Coder                                                             Description
2009-Nov-18Muthukkumaran Kaliyamoorhty                        created
2012-sep-8Muthukkumaran Kaliyamoorhty                        updatesStats date
*************************All the SQL keywords should be written in upper case*************************
*/AS
BEGIN
SET NOCOUNT ON
DECLARE
@db_name SYSNAME,
@tab_name SYSNAME,
@ind_name VARCHAR(500),
@schema_name SYSNAME,
@frag FLOAT,
@pages INT,
@min_id INT,
@max_id INT,
@stats_date VARCHAR(8)

SET @db_name=@p_dbname

--------------------------------------------------------------------------------------------------------------------------------------
--inserting the Fragmentation details
--------------------------------------------------------------------------------------------------------------------------------------
CREATE TABLE #tempfrag
(
id INT IDENTITY,
table_name SYSNAME,
index_name VARCHAR(500),
frag FLOAT,
pages INT,
schemaName SYSNAME,
statsDate VARCHAR(8)
)


EXEC ('USE ['+@db_name+'];
INSERT INTO #tempfrag (table_name,index_name,frag,pages,schemaName,statsDate)
SELECT OBJECT_NAME(F.OBJECT_ID) obj,i.name ind,
f.avg_fragmentation_in_percent,
f.page_count,OBJECT_SCHEMA_NAME(f.object_id),
CONVERT(VARCHAR(8),STATS_DATE(I.OBJECT_ID,I.index_id),112)as statsdate
FROM SYS.DM_DB_INDEX_PHYSICAL_STATS (DB_ID(),NULL,NULL,NULL,NULL) F
JOIN SYS.INDEXES I
ON(F.OBJECT_ID=I.OBJECT_ID)AND i.index_id=f.index_id
JOIN INFORMATION_SCHEMA.TABLES S
ON (s.table_name=OBJECT_NAME(F.OBJECT_ID))
AND f.database_id=DB_ID()
AND OBJECTPROPERTY(I.OBJECT_ID,''ISSYSTEMTABLE'')=0
AND alloc_unit_type_desc = ''IN_ROW_DATA''
AND is_disabled = 0
'

)
SELECT @min_id=MIN(ID)FROM #tempfrag
SELECT @max_id=MAX(ID)FROM #tempfrag
TRUNCATE TABLE msdb.dbo.dba_defrag_maintenance_history

--SELECT * FROM #tempfrag

WHILE (@min_id<=@max_id)
/*While loop begin*/BEGIN
SELECT
@tab_name=table_name,
@schema_name=schemaname,
@ind_name=index_name ,
@frag=frag ,
@pages=pages,
@stats_date=statsDate
FROM #tempfrag WHERE id = @min_id

--------------------------------------------------------------------------------------------------------------------------------------
--Check the fragmentation greater than 30% and pages greater than 1000 then rebuild
--------------------------------------------------------------------------------------------------------------------------------------
IF (@ind_name IS NOT NULL)
/*First if condition*/BEGIN
IF (@frag>30 AND @pages>1000)
/*Sub if condition 1st */BEGIN
EXEC ('USE ['+@db_name+'];ALTER INDEX ['+@ind_name+'] ON ['+@schema_name+'].['+@tab_name +'] REBUILD ')
INSERT INTO msdb.dbo.dba_defrag_maintenance_history
VALUES (@db_name,@tab_name,@ind_name,@frag,@pages,'REBUILD',@stats_date,GETDATE())
END
--------------------------------------------------------------------------------------------------------------------------------------
--Check the fragmentation between 5% to 30% and pages greater than 1000 then reorganize
--------------------------------------------------------------------------------------------------------------------------------------
ELSE IF((@frag BETWEEN 5 AND 30) AND @pages>1000  )
/*Sub if condition 2nd */BEGIN
BEGIN TRY
EXEC ('USE ['+@db_name+'];ALTER INDEX ['+@ind_name+'] ON ['+@schema_name+'].['+@tab_name +'] REORGANIZE ')
IF (@stats_date <CONVERT(VARCHAR(8),DATEADD(HH,1,GETDATE()-10),112))
BEGIN
EXEC ('USE ['+@db_name+'];UPDATE STATISTICS ['+@schema_name+'].['+@tab_name+'] (['+@ind_name+']) ' )
INSERT INTO msdb.dbo.dba_defrag_maintenance_history
VALUES (@db_name,@tab_name,@ind_name,@frag,@pages,'REORGANIZE & UPDATESTATS',@stats_date,GETDATE())
END
END TRY
BEGIN CATCH
--------------------------------------------------------------------------------------------------------------------------------------
--Check the fragmentation between 15% to 29% and pages greater than 1000 and page level
--lock disabled then rebuild
--------------------------------------------------------------------------------------------------------------------------------------
IF ERROR_NUMBER()=2552
EXEC ('USE ['+@db_name+'];ALTER INDEX ['+@ind_name+'] ON ['+@schema_name+'].['+@tab_name +'] REBUILD ')
INSERT INTO msdb.dbo.dba_defrag_maintenance_history
VALUES (@db_name,@tab_name,@ind_name,@frag,@pages,'PLLD_REBUILD',@stats_date,GETDATE())
END CATCH
END

--------------------------------------------------------------------------------------------------------------------------------------
--Update the statistics  for all indexes if the first three conditions is false
--------------------------------------------------------------------------------------------------------------------------------------
/*Sub if condition 3rd*/ELSE
BEGIN
IF (@stats_date < CONVERT(VARCHAR(8),DATEADD(HH,1,GETDATE()-10),112))
BEGIN
EXEC ('USE ['+@db_name+'];UPDATE STATISTICS ['+@schema_name+'].['+@tab_name+'] (['+@ind_name+']) '  )
INSERT INTO msdb.dbo.dba_defrag_maintenance_history
VALUES (@db_name,@tab_name,@ind_name,@frag,@pages,'UPDATESTATS',@stats_date,GETDATE())
END
END
END
ELSE

BEGIN
IF (@stats_date < CONVERT(VARCHAR(8),DATEADD(HH,1,GETDATE()-10),112))
BEGIN
--------------------------------------------------------------------------------------------------------------------------------------
--Update the statistics  for all tables if the first three conditions is false
--------------------------------------------------------------------------------------------------------------------------------------
EXEC ('USE ['+@db_name+'];UPDATE STATISTICS ['+@schema_name+'].['+@tab_name+']')
INSERT INTO msdb.dbo.dba_defrag_maintenance_history
VALUES (@db_name,@tab_name,'HEAP',@frag,@pages,'UPDATESTATS',@stats_date,GETDATE())
END
/*End of the first if condition*/END

SET @min_id=@min_id+1
/*While loop end*/END

DROP TABLE #tempfrag
INSERT INTO master.dbo.dba_defrag_maintenance_history
SELECT * FROM msdb.dbo.dba_defrag_maintenance_history
END
GO

Rate

3.2 (20)

You rated this post out of 5. Change rating

Share

Share

Rate

3.2 (20)

You rated this post out of 5. Change rating