BPSoftware.com
Home   Utilities   Purchase   FAQ   Support   Contact        
Shareware Utilities
 APrintDirect
 AIconExtract
 AFile Attribute Manager
Freeware Utilities
 AddrMon
 AFileSync
 ASysIcon
 B&P Table Utilities
 BPACLer
 BPSNMPMon
 BPSNMPUtil
 CharCount
 Delphi® Components
 MacAddr
Miscellaneous
 BPSoftware Blog
 Purchase Shareware
 Support

 Subscribe!

Monday, November 24, 2008
SQL: Index Fragmentation Maintenance

In a previous post, I discussed SQL Server's built in ability for suggesting missing indexes. As records are inserted, updated and deleted from tables indexes can become fragmented. A fragmented index affects performance (table scans and index seeks) when the DBMS needs to search for records.

This probably leaves the question, "How can one defragment the indexes of SQL tables?"

Within SQL Server 2005 and greater you can setup a maintenance plan to defragment all the indexes within all or a particular database. You can also right click on an index and select to either Rebuild or Reorganize and index (rebuilding will re-create the index whereas reorganize reorders the index pages). I prefer to have a little more control and use the following script as part of my maintenance plan. This script allows for a little more control in the selecting of tables, indexes and databases to maintain. I tend to rebuild indexes that are more than 30% fragmented and reorganize those that are greater than 5%.

SQL Server 2005 (and greater) has index information more readily accessible. The key to this script is the index information that is contained in sys.dm_db_index_physical_stats. This table returns the size and fragmentation information for each index.
1/** **/
2-- Create table to hold index information
3SET NOCOUNT ON;
4
5create table _IndexInformation (
6 database_id smallint,
7 ObjectName varchar(255),
8 [object_id] int,
9 IndexName varchar(255),
10 [index_id] int,
11 index_level int,
12 index_type_desc varchar(60),
13 avg_fragmentation_in_percent float,
14 fragment_count float,
15 page_count bigint,
16 record_count bigint,
17 index_depth int
18);
19/** **/

1/** **/
2-- clear table contents
3truncate table _IndexInformation;
4
5-- populate table
6insert into _IndexInformation
7 select
8 database_id,
9 object_name(i.object_id) AS [ObjectName],
10 i.[object_id],
11 i.name AS [IndexName],
12 dm.index_id,
13 dm.index_level,
14 dm.index_type_desc,
15 dm.avg_fragmentation_in_percent,
16 dm.fragment_count,
17 dm.page_count,
18 dm.record_count,
19 dm.index_depth
20 from
21 -- database, table and index information can be specified
22 /*
23 sys.dm_db_index_physical_stats (
24 { database_id NULL 0 DEFAULT }
25 , { object_id NULL 0 DEFAULT }
26 , { index_id NULL 0 -1 DEFAULT }
27 , { partition_number NULL 0 DEFAULT }
28 , { mode NULL DEFAULT }
29 )
29 This example uses database 1; you should replace this with the ID of the database you wish to process
30 */

31 sys.dm_db_index_physical_stats(1,null,null,null,'detailed') dm
32 INNER JOIN sys.indexes i ON
33 (i.object_id = dm.object_id) AND (i.index_id = dm.index_id);
34/** **/
35

1/** **/
2-- read index table and maintain indexes
3SET NOCOUNT ON;
4SET ANSI_PADDING ON;
5SET ANSI_NULLS ON;
6SET QUOTED_IDENTIFIER ON;
7
8
9-- Declare variables.
10declare @tablename as varchar(255)
11declare @indexname as varchar(255)
12declare @frag as float
13declare @cmd as varchar(1024)
14
15-- Declare a cursor.
16declare indexes insensitive scroll cursor
17for
18 select
19 ObjectName,
20 IndexName,
21 avg_fragmentation_in_percent
22 from
23 _IndexInformation
24 where
25 (avg_fragmentation_in_percent >= 5);
26
27-- Open the cursor.
28open indexes
29
30
31-- Loop through all the indexes.
32while (1=1)
33 begin
34 fetch next from indexes into @tablename, @indexname, @frag;
35 if @@fetch_status <> 0 break;
36 -- if fragmentation is above 30% rebuild index
37 -- if fragmentation is above 5% reorganize index
38 if (@frag >= 30)
39 begin
40 set @cmd = 'ALTER INDEX [' + rtrim(@indexname) + '] ON [' + @tablename +'] REBUILD;'
41 exec (@cmd)
42 print @cmd;
43 end
44 else
45 if (@frag >= 5)
46 begin
47 set @cmd = 'ALTER INDEX [' + rtrim(@indexname) + '] ON [' + @tablename +'] REORGANIZE;'
48 exec (@cmd)
49 print @cmd;
50 end;
51 end
52
53
54
55-- Close cursor
56close indexes;
57deallocate indexes;
58/** **/
59

1/** **/
2-- delete index table
3drop table _IndexInformation;
4/** **/
5


I have the script schedule to as part of my database maintenance plan. I run the script several times with additional selection criteria limiting it to groups of tables in a particular database.

Labels: ,

posted by Brad Prendergast at 9:51:00 PM
Comments:
Shouldn't you specify database id in sys.dm_db_index_physical_stats query (second listing row 31)?

Otherwise you get funny results as object_id's are database specific.
posted by Anonymous Anonymous Tuesday, November 25, 2008 7:28:00 AM  
The use of sys.dm_db_index_physical_stats does not require a database, however in this case that is how it is used. The actual production code does specify a database; it omitted it for example. I will add a reference back in and a comment indicating such. Thanks!
posted by Blogger Brad Prendergast Tuesday, November 25, 2008 7:34:00 AM  
Links to this post:

Create a Link

Recent Posts
 Off-topic: Uhm, Rickroll?
 SQL: Where are the database files?
 Show Desktop in my QuickLaunch Toolbar?
 Command Line: Visual Source Safe
 SQL: Remove / Delete Orphan Users
 SQL Delete/Drop a User from each Database
 Is there an 'I' in phone?
 SQL Optimization: Am I missing any indexes? - Part...
 Meaningful Signature File Quotes
 My Shared RSS Items

 Subscribe!


Labels



Archives
 October 2005
 November 2005
 December 2005
 January 2006
 February 2006
 March 2006
 April 2006
 May 2006
 June 2006
 July 2006
 August 2006
 September 2006
 December 2006
 January 2007
 February 2007
 March 2007
 September 2007
 October 2007
 November 2007
 July 2008
 November 2008
Powered by Blogger