Defragment Indexes stored procedure - new version
I have fixed some bugs in my isp_ALTER_INDEX stored procedure. Use this version instead of the old one.
This stored procedure defragments indexes in SQL Server 2005. It utilizes sys.dm_db_index_physical_stats, a dynamic management function, to retrieve the fragmentation levels.
If you plan to use DETAILED for @statsMode, you may want to consider running the query that builds the #FragIndex table on a database snapshot. Kalen Delaney mentions this in the May 2006 edition of SQL Server Magazine (InstantDoc ID 49769).
------------------------------------------------------------------------------------------- -- OBJECT NAME : isp_ALTER_INDEX -- -- AUTHOR : Tara Kizer -- -- DATE : February 27, 2007 -- -- INPUTS : @dbName - name of the database -- @statsMode - LIMITED, SAMPLED or DETAILED -- @defragType - REORGANIZE (INDEXDEFRAG) or REBUILD (DBREINDEX) -- @minFragPercent - minimum fragmentation level -- @maxFragPercent - maximum fragmentation level -- @minRowCount - minimum row count -- -- OUTPUTS : None -- -- DEPENDENCIES : sys.dm_db_index_physical_stats, sys.objects, -- sys.schemas, sys.indexes, sys.partitions -- -- DESCRIPTION : Defragments indexes /* EXEC isp_ALTER_INDEX @dbName = 'DatabaseName', @statsMode = 'SAMPLED', @defragType = 'REBUILD', @minFragPercent = 10, @maxFragPercent = 100, @minRowCount = 1000 */ ------------------------------------------------------------------------------------------- CREATE PROC [dbo].[isp_ALTER_INDEX] ( @dbName sysname, @statsMode varchar(8) = 'SAMPLED', @defragType varchar(10) = 'REORGANIZE', @minFragPercent int = 25, @maxFragPercent int = 100, @minRowCount int = 0 ) ASSET NOCOUNT ON
IF @statsMode NOT IN ('LIMITED', 'SAMPLED', 'DETAILED') BEGIN RAISERROR('@statsMode must be LIMITED, SAMPLED or DETAILED', 16, 1) RETURN END
IF @defragType NOT IN ('REORGANIZE', 'REBUILD') BEGIN RAISERROR('@defragType must be REORGANIZE or REBUILD', 16, 1) RETURN END
DECLARE @i int, @objectId int, @objectName sysname, @indexId int, @indexName sysname, @schemaName sysname, @partitionNumber int, @partitionCount int, @sql nvarchar(4000), @edition int, @parmDef nvarchar(500), @allocUnitType nvarchar(60), @indexType nvarchar(60), @online bit, @disabled bit
SELECT @i = 1, @edition = CONVERT(int, SERVERPROPERTY('EngineEdition'))
SELECT IDENTITY(int, 1, 1) AS FragIndexId, [object_id] AS ObjectId, index_id AS IndexId, avg_fragmentation_in_percent AS FragPercent, record_count AS RecordCount, partition_number AS PartitionNumber, index_type_desc AS IndexType, alloc_unit_type_desc AS AllocUnitType, 0 AS Online INTO #FragIndex FROM sys.dm_db_index_physical_stats (DB_ID(@dbName), NULL, NULL, NULL, @statsMode) WHERE avg_fragmentation_in_percent > @minFragPercent AND avg_fragmentation_in_percent < @maxFragPercent AND index_id > 0 ORDER BY ObjectId
– LIMITED does not include data for record_count IF @statsMode IN ('SAMPLED', 'DETAILED') DELETE FROM #FragIndex WHERE RecordCount < @minRowCount
– Developer and Enterprise have the ONLINE = ON option for REBUILD – Indexes, including indexes on global temp tables, can be rebuilt online with the following exceptions: – Disabled indexes, XML indexes, Indexes on local temp tables, Partitioned indexes, – Clustered indexes if the underlying table contains LOB data types, – Nonclustered indexes that are defined with LOB data type columns IF @defragType = 'REBUILD' AND @edition = 3 BEGIN UPDATE #FragIndex SET Online = 1
<span class="kwrd">UPDATE</span> #FragIndex <span class="kwrd">SET</span> Online = <span class="kwrd">CASE</span> <span class="kwrd">WHEN</span> IndexType = <span class="str">'XML INDEX'</span> <span class="kwrd">THEN</span> 0 <span class="kwrd">WHEN</span> IndexType = <span class="str">'NONCLUSTERED INDEX'</span> <span class="kwrd">AND</span> AllocUnitType = <span class="str">'LOB_DATA'</span> <span class="kwrd">THEN</span> 0 <span class="kwrd">ELSE</span> 1 <span class="kwrd">END</span> <span class="kwrd">UPDATE</span> f <span class="kwrd">SET</span> Online = 0 <span class="kwrd">FROM</span> #FragIndex f <span class="kwrd">JOIN</span> ( <span class="kwrd">SELECT</span> [object_id] <span class="kwrd">AS</span> ObjectId, alloc_unit_type_desc <span class="kwrd">AS</span> AllocUnitType <span class="kwrd">FROM</span> sys.dm_db_index_physical_stats (DB_ID(@dbName), <span class="kwrd">NULL</span>, <span class="kwrd">NULL</span>, <span class="kwrd">NULL</span>, <span class="kwrd">NULL</span>) ) s <span class="kwrd">ON</span> f.ObjectId = s.ObjectId <span class="kwrd">WHERE</span> f.IndexType = <span class="str">'CLUSTERED INDEX'</span> <span class="kwrd">AND</span> s.AllocUnitType = <span class="str">'LOB_DATA'</span> <span class="rem">-- we can't determine if the indexes are disabled or partitioned yet,</span> <span class="rem">-- so we'll need to figure that out during the loop</span>
END
SELECT @objectId = ObjectId, @indexId = IndexId, @partitionNumber = PartitionNumber, @indexType = IndexType, @online = Online FROM #FragIndex WHERE FragIndexId = @i
WHILE @@ROWCOUNT <> 0 BEGIN SET @sql = ' SELECT @objectName = o.[name], @schemaName = s.[name] FROM ' + @dbName + '.sys.objects o JOIN ' + @dbName + '.sys.schemas s ON s.schema_id = o.schema_id WHERE o.[object_id] = @objectId'
<span class="kwrd">SET</span> @parmDef = N<span class="str">'@objectId int, @objectName sysname OUTPUT, @schemaName sysname OUTPUT'</span> <span class="kwrd">EXEC</span> sp_executesql @<span class="kwrd">sql</span>, @parmDef, @objectId = @objectId, @objectName = @objectName <span class="kwrd">OUTPUT</span>, @schemaName = @schemaName <span class="kwrd">OUTPUT</span> <span class="kwrd">SET</span> @<span class="kwrd">sql</span> = <span class="str">' SELECT @indexName = [name], @disabled = is_disabled FROM '</span> + @dbName + <span class="str">'.sys.indexes WHERE [object_id] = @objectId AND index_id = @indexId'</span> <span class="kwrd">SET</span> @parmDef = N<span class="str">' @objectId int, @indexId int, @indexName sysname OUTPUT, @disabled bit OUTPUT'</span> <span class="kwrd">EXEC</span> sp_executesql @<span class="kwrd">sql</span>, @parmDef, @objectId = @objectId, @indexId = @indexId, @indexName = @indexName <span class="kwrd">OUTPUT</span>, @disabled = @disabled <span class="kwrd">OUTPUT</span> <span class="kwrd">SET</span> @<span class="kwrd">sql</span> = <span class="str">' SELECT @partitionCount = COUNT(*) FROM '</span> + @dbName + <span class="str">'.sys.partitions WHERE [object_id] = @objectId AND index_id = @indexId'</span> <span class="kwrd">SET</span> @parmDef = N<span class="str">'@objectId int, @indexId int, @partitionCount int OUTPUT'</span> <span class="kwrd">EXEC</span> sp_executesql @<span class="kwrd">sql</span>, @parmDef, @objectId = @objectId, @indexId = @indexId, @partitionCount = @partitionCount <span class="kwrd">OUTPUT</span> <span class="kwrd">SET</span> @<span class="kwrd">sql</span> = <span class="str">'ALTER INDEX ['</span> + @indexName + <span class="str">'] ON ['</span> + @dbName + <span class="str">'].['</span> + @schemaName + <span class="str">'].['</span> + @objectName + <span class="str">'] '</span> + @defragType <span class="kwrd">IF</span> @online = 1 <span class="kwrd">AND</span> @disabled = 0 <span class="kwrd">AND</span> @partitionCount = 1 <span class="kwrd">SET</span> @<span class="kwrd">sql</span> = @<span class="kwrd">sql</span> + <span class="str">' WITH (ONLINE = ON)'</span> <span class="kwrd">IF</span> @partitionCount > 1 <span class="kwrd">AND</span> @disabled = 0 <span class="kwrd">AND</span> @indexType <> <span class="str">'XML INDEX'</span> <span class="kwrd">SET</span> @<span class="kwrd">sql</span> = @<span class="kwrd">sql</span> + <span class="str">' PARTITION = '</span> + <span class="kwrd">CAST</span>(@partitionNumber <span class="kwrd">AS</span> <span class="kwrd">varchar</span>(10)) <span class="kwrd">EXEC</span> (@<span class="kwrd">SQL</span>) <span class="kwrd">SET</span> @i = @i + 1 <span class="kwrd">SELECT</span> @objectId = ObjectId, @indexId = IndexId, @partitionNumber = PartitionNumber, @indexType = IndexType, @online = Online <span class="kwrd">FROM</span> #FragIndex <span class="kwrd">WHERE</span> FragIndexId = @i
END
DROP TABLE #FragIndex
Legacy Comments
jayson knight
2007-04-19 |
re: Defragment Indexes stored procedure - new version Getting the following error: Msg 102, Level 15, State 1, Procedure isp_ALTER_INDEX, Line 62 Incorrect syntax near '('. Msg 102, Level 15, State 1, Procedure isp_ALTER_INDEX, Line 94 Incorrect syntax near '('. THoughts? |
Tara
2007-04-20 |
re: Defragment Indexes stored procedure - new version Jayson, I just ran this on a system where I don't have this stored procedure already and I don't encounter any errors when I do a direct copy/paste from this weblog. If you can't figure out what the problem is, send me a message and I'll send you the script. |
pootle_flump
2007-04-27 |
re: Defragment Indexes stored procedure - new version Hi Tara I am putting together our own defragging routine so I have been referring to yours :-) Thanks for the resource. Just a couple of points - if stats mode is LIMITED you get NULL row counts, so nothing will get defragged (....record_count > @minRowCount). Also, if stats mode is DETAILED you get one row per B-Tree level that meets the fragmentation threshold. I noticed this when my logging procedure was defragging indexes several times for each run. As such I don't have a stats mode parameter in my proc any more - it is hard coded as SAMPLED (like your default). HTH |
Tara
2007-04-27 |
re: Defragment Indexes stored procedure - new version Perhaps that's why my code is having issues on one particular database. I have had to disable it for now. Thanks for the info! |