Keeping your SQL Server turned requires that you perform routine maintenance on a regular basis. Some of those routines can be automated through a maintenance plan most importantly the shrinking and reorganizing of the database files. What sometimes is missed is the index fragmentation. If your indexes are fragmented it can cause performance issues on how the data searches through the leafs to get to the data it needs. I have included an excellent script you can run that will de-frag your indexes that have a 30% or more fragmentation. You can adjust the variables in the script to de-frag indexes with less than 30% fragmentation or you can just view the results. Remember if an index is not heap the fragmentation doesn’t matter, if the table has a low amount of page counts.
— Specify your Database Name
USE DatabaseName — Change to the database you want to defrag
GO
— Declare variables
SET NOCOUNT ON
DECLARE @tablename VARCHAR(128)
DECLARE @execstr VARCHAR(255)
DECLARE @objectid INT
DECLARE @indexid INT
DECLARE @frag decimal
DECLARE @maxfrag decimal
DECLARE @IdxName varchar(128)
DECLARE @ViewOnly bit
— Set to 1 to view proposed actions, set to 0 to Execute proposed actions:
SET @ViewOnly=1
— Decide on the maximum fragmentation to allow for.
SET @maxfrag = 30.0
— Declare a cursor.
DECLARE tables CURSOR FOR
SELECT CAST(TABLE_SCHEMA AS VARCHAR(100))
+’.’+CAST(TABLE_NAME AS VARCHAR(100))
AS Table_Name
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = ‘BASE TABLE’
— Create the temporary table.
if exists (select name from tempdb.dbo.sysobjects where name like ‘#fraglist%’)
drop table #fraglist
CREATE TABLE #fraglist (
ObjectName CHAR(255),
ObjectId INT,
IndexName CHAR(255),
IndexId INT,
Lvl INT,
CountPages INT,
CountRows INT,
MinRecSize INT,
MaxRecSize INT,
AvgRecSize INT,
ForRecCount INT,
Extents INT,
ExtentSwitches INT,
AvgFreeBytes INT,
AvgPageDensity INT,
ScanDensity decimal,
BestCount INT,
ActualCount INT,
LogicalFrag decimal,
ExtentFrag decimal)
— Open the cursor.
OPEN tables
— Loop through all the tables in the database.
FETCH NEXT
FROM tables
INTO @tablename
WHILE @@FETCH_STATUS = 0
BEGIN
— Do the showcontig of all indexes of the table
INSERT INTO #fraglist
EXEC (‘DBCC SHOWCONTIG (”’ + @tablename + ”’)
WITH FAST, TABLERESULTS, ALL_INDEXES, NO_INFOMSGS’)
FETCH NEXT
FROM tables
INTO @tablename
END
— Close and deallocate the cursor.
CLOSE tables
DEALLOCATE tables
— Declare the cursor for the list of indexes to be defragged.
DECLARE indexes CURSOR FOR
SELECT ObjectName, ObjectId, IndexId, LogicalFrag, IndexName
FROM #fraglist
WHERE LogicalFrag >= @maxfrag
AND INDEXPROPERTY (ObjectId, IndexName, ‘IndexDepth’) > 0
— Open the cursor.
OPEN indexes
— Loop through the indexes.
FETCH NEXT
FROM indexes
INTO @tablename, @objectid, @indexid, @frag, @IdxName
WHILE @@FETCH_STATUS = 0
BEGIN
IF (@ViewOnly=1)
BEGIN
PRINT ‘WOULD be executing ALTER INDEX ‘ + RTRIM(@IdxName) + ‘ ON ‘ + RTRIM(@tablename) + ‘ REORGANIZE WITH ( LOB_COMPACTION = ON ) – Fragmentation currently ‘ + RTRIM(CONVERT(VARCHAR(15),@frag)) + ‘%’
END
ELSE
BEGIN
PRINT ‘Now executing ALTER INDEX ‘ + RTRIM(@IdxName) + ‘ ON ‘ + RTRIM(@tablename) + ‘ REORGANIZE WITH ( LOB_COMPACTION = ON ) – Fragmentation currently ‘ + RTRIM(CONVERT(VARCHAR(15),@frag)) + ‘%’
SELECT @execstr = ‘ALTER INDEX ‘ + RTRIM(@IdxName) + ‘ ON ‘ + RTRIM(@tablename) + ‘ REORGANIZE WITH ( LOB_COMPACTION = ON )’
EXEC (@execstr)
END
FETCH NEXT
FROM indexes
INTO @tablename, @objectid, @indexid, @frag, @IdxName
END
— Close and deallocate the cursor.
CLOSE indexes
DEALLOCATE indexes
— Delete the temporary table.
DROP TABLE #fraglist
GO
Tim Lally has been working with SQL Server and ERP systems since 1996. His experience spans over 25 years and includes developing systems using a variety of development languages and systems, for businesses around the world.