Defragmenting SQL Server 2005 and 2008 using dynamic management views
You can read all about SQL defragmentation here by Satya Jayanty. The bottom line - defragmentation is bad. It causes high CPU load and poor SQL performance. Shrinking your database causes massive index fragmentation. You can read about that here, therefore you should always turn off auto-shrink (I also have a script for that here).
For SQL 2000 you must use DBCC SHOWCONTIG. You can use the SQL given here.
However, for SQL Server 2005 onwards, there is a better way using the dynamic management view sys.dm_db_index_physical_stats introduced in SQL 2005.
Building that into a stored proc to execute every so often to defrag gives us:
Download code here.
For SQL 2000 you must use DBCC SHOWCONTIG. You can use the SQL given here.
However, for SQL Server 2005 onwards, there is a better way using the dynamic management view sys.dm_db_index_physical_stats introduced in SQL 2005.
Note: In SQL 2009 this proc is not going to work because Microsoft are going to remove DBCC INDEXDEFRAG from the next version of Microsoft SQL Server (read 2009), you need to user ALTER INDEX instead, as shown in my next blog entry.
SELECT OBJECT_NAME(i.object_id) AS TableName,
i.name AS TableIndexName,phystat.avg_fragmentation_in_percent,
phystat.fragment_count
FROM sys.dm_db_index_physical_stats(DB_ID('your_database_name'), NULL, NULL, NULL, 'LIMITED') phystat
INNER JOIN sys.indexes iON i.object_id = phystat.object_id AND i.index_id = phystat.index_id
WHERE phystat.avg_fragmentation_in_percent > 10ORDER BY phystat.avg_fragmentation_in_percent DESC
Building that into a stored proc to execute every so often to defrag gives us:
CREATE PROCEDURE sjh_defragment_indexes(
@maxfrag FLOAT)
AS/* Simon Hughes 15 May 2010For SQL 2005+ onlyThis stored procedure checks index fragmentation in a database and defragmentsindexes whose fragmentation fall above a specified threshold: @maxfragMust be run in the database to be defragmented.Please change 'your_database_name' to be the name of your database exec sjh_defragment_indexes 5.0*/SET NOCOUNT ONSET XACT_ABORT ON
-- Check this is not being run in a system databaseIF DB_NAME() IN ('master', 'msdb', 'model', 'tempdb')
BEGINPRINT 'This procedure should not be run in system databases.'
RETURNEND-- Check fragmentationSELECT OBJECT_NAME(i.object_id) AS TableName,
i.name AS TableIndexName,phystat.avg_fragmentation_in_percent,
phystat.fragment_count,
'DBCC INDEXDEFRAG (0, [' + OBJECT_NAME(i.object_id) + '], ' + i.name + ') WITH NO_INFOMSGS' AS execstr
INTO [#frag]FROM sys.dm_db_index_physical_stats(DB_ID('your_database_name'), NULL, NULL, NULL, 'LIMITED') phystat
INNER JOIN sys.indexes i ON i.object_id = phystat.object_id AND i.index_id = phystat.index_idWHERE i.name IS NOT NULL
AND phystat.avg_fragmentation_in_percent > @maxfragORDER BY phystat.avg_fragmentation_in_percent DESC
SELECT * FROM [#frag]
-- Write start time for information purposesPRINT 'Started defragmenting at: ' + CONVERT(VARCHAR, GETDATE())
-- Begin defragDECLARE @execstr NVARCHAR(1024)DECLARE cc CURSOR FAST_FORWARD FOR
SELECT execstr FROM [#frag]
OPEN ccFETCH NEXT FROM cc INTO @execstr
WHILE @@FETCH_STATUS = 0 BEGIN EXEC sp_executesql @execstrFETCH NEXT FROM cc INTO @execstr
END-- Close and deallocate the cursorCLOSE ccDEALLOCATE cc-- Report on finish time for information purposesPRINT 'Finished defragmenting at: ' + CONVERT(VARCHAR, GETDATE())
-- Delete the temporary tableDROP TABLE [#frag]GO
Download code here.