Technical Article

For SQL 2000.Scan all indexes for fragmentation

,

This script creates an error message with a valid error number, and alert associated with the error message.
(everything when it doesn't exists). Then captures all the user indexes in the database and
executes DBCC ShowContig for each one. It calculates the % of fragmentation on leaf level and fires the alert when is fragmented.
Then saves a log file with all the scanned indexes and info for the ones that need to be defrag.
   It can be inserted in a job to check on schedule for fragmentation on indexes, or executed on demand.

/*
**  Author: Rodrigo Acosta
**  Email: acosta_rodrigo@hotmail.com
**
**  Script to be included in a Job that manages fragmentation
**  in all indexes in the executing database.
*/

Set Nocount On

/*
** *************Error Message Definition********************
*/
/* Declare Variables that wil be used for the error message */Declare @Error_no Int
Declare @AddMessage Varchar(1500)

/* Calculates the last error number of the message */Set @Error_no = (Select Top 1 error From master.dbo.sysmessages Order by error Desc)

/*
   First checks if the message that will be added exists.
   If it doesn't, calculates the available error number 
   and creates the message.
*/If Not Exists
(Select description from master.dbo.sysmessages
Where description Like ('%View C:\Frag_Info.log for more info.'))
Begin
/* 
   If no user define error messages were added (Error < 50001).
   Creates the message with the number 50001.
*/If @Error_no < 50001
Begin
Set @Error_no = 50001
Set @AddMessage = 'EXEC Sp_addmessage @msgnum=' + Convert(VarChar(5), @Error_no) + ', 
   @severity=19,
   @msgtext="%s index %s on table %s is %s percent fragmented. Consider re-building the index. View C:\Frag_Info.log for more info.",
   @with_log=True'
EXEC (@AddMessage)
End
Else
/*
   If there is an used define error (Error > 50001). Calculates the
   first available error number and creates the error message.
*/Begin
Set @Error_no = @Error_no + 1
Set @AddMessage='EXEC Sp_addmessage @msgnum=' + Convert(VarChar(5), @Error_no) + ', 
   @severity=19,
   @msgtext="%s index %s on table %s is %s percent fragmented. Consider re-building the index. View C:\Frag_Info.log for more info.",
   @with_log=True'
EXEC (@AddMessage)
End
End

/*
** *************Alert Definition********************
*/
/*
** If the Alerts Does not exists, it is created to response to the Error message 
** created before.
*/If Not Exists
(Select Name From msdb.dbo.sysalerts Where name = 'Fragmentation above 50% in user index.')
Begin
EXECUTE msdb.dbo.sp_add_alert @name = 'Fragmentation above 50% in user index.',
@message_id = @Error_no,
@Severity = 0,
@Enabled = 1,
@delay_between_responses = 0,
@category_name = '[Uncategorized]'
End


/*
** *************Selecting Sysindexes columns********************
*/

/*  Creates a Temp Table to hold the results from DBCC ShowContig   */
/*  Creates a Temp Table to show the results from DBCC ShowContig  */Create Table #SaveResults
(ObjectName Sysname,
ObjectId int,
IndexName Sysname,
Indexid tinyint,
[Level] int,
Pages Int,
[Rows] Int,
MinimumRecordSize Int,
MaximumRecordSize Int,
AverageRecordSize Int,
ForwardedRecords Int,
Extents Int,
ExtentSwitches Int,
AverageFreeBytes Int,
AveragePageDensity Int,
ScanDensity Int,
BestCount tinyint,
ActualCount TinyInt,
LogFragmentation TinyInt,
ExtentFragmentation Int)

/*  Creates a Temp Table to show the modified results from DBCC ShowContig   */Create Table ##ShowResults
(TableName sysname,
IndexName sysname,
IndexType Varchar (12),
[%Frag] TinyInt,
Defrag VarChar (3),
Pages Int,
AvgFreeBytes Int)

/* 
   Declares a cursor and variables that holds the table and index Type  (Clustered or Nonclustered) 
   and the FillFactor (original) from user tables with indexes.
*/   
Declare @TableName sysname
Declare @IndexName sysname
Declare vCursor Cursor For
Select Object_name (i.id) As 'TableName',
i.name As 'IndexName' 
From Sysindexes i Inner Join sysobjects o
On i.id=o.id
Where (Indid = 1 Or Indid Between 2 And 250) And xtype = 'U'

Declare @cmdDBCC Varchar (200)


Open vCursor
Fetch Next From vCursor Into @TableName, @IndexName
While @@Fetch_Status = 0
Begin
Set @cmdDBCC = 'DBCC ShowContig (' + @TableName + ', ' + @IndexName + 
') With TableResults'
Insert #SaveResults
Exec (@cmdDBCC)
/*  Fills the #SaveResults Table with all the results of the execution*/Fetch Next From vCursor Into @TableName, @IndexName
End

/*  Close the cursor because it don't use it anymore   */Close vCursor
Deallocate Vcursor

/*  Declare variables to save info from the #SaveResults   */Declare @ObjectName Sysname  
Declare @IndexName2 Sysname
Declare @ScanDensity Int
Declare @IndexType Varchar(13)
Declare @Pages  Int
Declare @AverageFreeBytes Int
Declare @Rows Int
Declare @Defrag Char(3)
Declare vCursor2 Cursor For 
Select ObjectName, IndexName, ScanDensity, 
[IndexType]=
Case
When IndexId = 1 Then 'Clustered'
Else 'NonClustered'
End
, 
Pages, [Rows], AverageFreeBytes
From #SaveResults
Open vCursor2
Fetch Next From vCursor2 Into @ObjectName, @IndexName2, @ScanDensity, @IndexType, 
@Pages, @Rows, @AverageFreeBytes

While @@Fetch_Status = 0
Begin
/* If fragmentation is above 50 % Then fire the error */If @ScanDensity < 50 
Begin
/* Builts the raise error sentence  */Declare @Raise Varchar(150)
Set @Defrag = 'Yes'
Set @Raise = 'Raiserror (' + Convert(Varchar(10),@Error_no) +
', 18, 1,' + ''''  + @IndexType + '''' + ',' + '''' +  @IndexName2+ '''' + ',' + '''' +
 @ObjectName + '''' + ',' + '''' + Convert(Varchar(20),@ScanDensity) + '''' + ')'
EXEC (@Raise)
End
/*  If not just alter the variable  to indicate no defragmentation is needed*/Else
Begin
Set @Defrag = 'No'
End

/*  Insert the results into the temp table created to show the results  */Insert ##ShowResults
Values
(@ObjectName, @IndexName2, @IndexType, 
@ScanDensity, @Defrag, @Pages, 
@AverageFreeBytes)

Fetch Next From vCursor2 Into @ObjectName, @IndexName2, @ScanDensity, @IndexType, 
@Pages, @Rows, @AverageFreeBytes
End

Close vCursor2
Deallocate vCursor2

Drop Table #SaveResults

/*
** *************Log File generation********************
*/Declare @cmd Varchar(1000)
Set @cmd=' EXEC master.dbo.xp_cmdShell ' + '''' + 
'OSQL -E -q"Set Nocount On Select Substring(@@ServerName,1,20) as [Executed On Server:] Select Substring(db_name(),1,20) ' + 
'AS [Executed On Database:] Select Getdate() as [LogFile generated with the ' + 
'results of fragmentation in all user Indexes. Date:] Select ' + 
'SubString(TableName,1,20) as TableName,IndexType, Substring(IndexName,1,20) ' + 
'as IndexName, [%Frag] as [%Defrag] , Defrag As [Need Defrag.?]  from ##ShowResults" -oC:\Frag_Info.log' + ''''
EXEC (@cmd)
Drop Table ##ShowResults

Rate

5 (1)

You rated this post out of 5. Change rating

Share

Share

Rate

5 (1)

You rated this post out of 5. Change rating