Rebuild indexes dynamically for all databases having fragmentation level more than defined level

Rebuild indexes dynamically for all databases

In this article we are going to learn how we can rebuild indexes for all the databases having fragmentation level more than defined level. Fragmentation level to rebuild the indexes may vary databases to database and In our example we are assuming it as 20%. Which can be suitable for most database servers.
We will also log the details of the indexes before rebuilding them and after rebuilding them in a table to get any report in near future.
And we will exclude all those indexes which have page counts less than 8, i.e. the table is too small and can fit in various different locations intentionally by DB engine.

Log Table Script:

CREATE TABLE MyDB.dbo.cmn_RebuildIndexesLog
(
 in_BatchNo INT,
 in_DBId INT,
 vc_DBName VARCHAR(256),
 vc_SchemaName VARCHAR(256),
 vc_TableName VARCHAR(256),
 vc_IndexName VARCHAR(256),
 num_AvgFragPercent NUMERIC(18,2),
 vc_TypeDesc CHAR(1) --Will store 'B' for before rebuild, 'A' for after rebuild
)

Procedure To build indexes dynamically for all databases:


CREATE PROCEDURE usp_cmn_RebuildIndexes
AS
BEGIN
DECLARE @in_BatchNo INT
SELECT @in_BatchNo = (ISNULL(MAX(in_BatchNo),0) + 1) FROM MyDB.dbo.cmn_RebuildIndexesLog

DECLARE @vc_DBName VARCHAR(200)
DECLARE @in_DBID INT

DECLARE cur_DBList CURSOR FOR
SELECT NAME, DATABASE_ID FROM SYS.DATABASES
WHERE NAME NOT IN
('master', 'tempdb', 'model', 'msdb', 'ReportServer', 'ReportServerTempDB') --Add if want to exclude any other too

OPEN cur_DBList
FETCH NEXT FROM cur_DBList INTO @vc_DBName, @in_DBID
WHILE (@@FETCH_STATUS = 0)
BEGIN
DECLARE @nvc_Qry NVARCHAR(4000)
SET @nvc_Qry = N'USE ' + @vc_DBName + ';
DECLARE @vc_TableName VARCHAR(250)
DECLARE @vc_IndexName VARCHAR(250)
DECLARE @vc_SchemaName VARCHAR(100)
DECLARE @nvc_ExecQry NVARCHAR(1100)

INSERT INTO MyDB.dbo.cmn_RebuildIndexesLog
(in_BatchNo, in_DBId,vc_DBName,vc_SchemaName,vc_TableName,vc_IndexName,num_AvgFragPercent,vc_TypeDesc)
SELECT ' + CAST(@in_BatchNo AS VARCHAR(5)) + ' AS in_BatchNo
,''' + CAST(@in_DBID AS VARCHAR(5)) + ''' AS DBId, ''' + @vc_DBName + ''' AS DBName
,SCHEMA_NAME(O.SCHEMA_ID) SchemaName
,OBJECT_NAME(D.OBJECT_ID) AS [TableName]
,I.NAME AS [IndexName], AVG_FRAGMENTATION_IN_PERCENT, ''B'' AS vc_TypeDesc
FROM SYS.DM_DB_INDEX_PHYSICAL_STATS(' + CAST(@in_DBID AS VARCHAR(5)) + ', NULL, -1, NULL, NULL) D
INNER JOIN SYS.INDEXES I ON I.OBJECT_ID=D.OBJECT_ID AND I.INDEX_ID = D.INDEX_ID
INNER JOIN SYS.OBJECTS O ON I.OBJECT_ID = O.OBJECT_ID
WHERE AVG_FRAGMENTATION_IN_PERCENT >= 20 AND I.NAME IS NOT NULL
AND D.PAGE_COUNT > 8

DECLARE cur_IndexDetails CURSOR FOR
SELECT
vc_SchemaName AS SchemaName,
vc_TableName AS [TableName]
,vc_IndexName AS [IndexName]
FROM MyDB.dbo.cmn_RebuildIndexesLog
WHERE in_BatchNo = ' + CAST(@in_BatchNo AS VARCHAR(5)) + '
AND in_DBId = ' + CAST(@in_DBID AS VARCHAR(5)) + ' AND vc_TypeDesc = ''B''

OPEN cur_IndexDetails
FETCH NEXT FROM cur_IndexDetails INTO @vc_SchemaName, @vc_TableName, @vc_IndexName
WHILE (@@FETCH_STATUS = 0)
BEGIN
SET @nvc_ExecQry = N''ALTER INDEX ['' + @vc_IndexName + ''] ON ['' + @vc_SchemaName + ''].['' + @vc_TableName + '']
REBUILD
WITH ( PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON, ONLINE = OFF, SORT_IN_TEMPDB = ON )''
EXEC SP_EXECUTESQL @nvc_ExecQry
FETCH NEXT FROM cur_IndexDetails INTO @vc_SchemaName, @vc_TableName, @vc_IndexName
END
CLOSE cur_IndexDetails
DEALLOCATE cur_IndexDetails

INSERT INTO MyDB.dbo.cmn_RebuildIndexesLog
(in_BatchNo, in_DBId,vc_DBName,vc_SchemaName,vc_TableName,vc_IndexName,num_AvgFragPercent,vc_TypeDesc)
SELECT ' + CAST(@in_BatchNo AS VARCHAR(5)) + ' AS in_BatchNo,
''' + CAST(@in_DBID AS VARCHAR(5)) + ''' AS DBId, ''' + @vc_DBName + ''' AS DBName
,SCHEMA_NAME(O.SCHEMA_ID) SchemaName,
OBJECT_NAME(D.OBJECT_ID) AS [TableName]
,I.NAME AS [IndexName], AVG_FRAGMENTATION_IN_PERCENT, ''A'' AS vc_TypeDesc
FROM SYS.DM_DB_INDEX_PHYSICAL_STATS(' + CAST(@in_DBID AS VARCHAR(5)) + ', NULL, -1, NULL, NULL) D
INNER JOIN SYS.INDEXES I ON I.OBJECT_ID=D.OBJECT_ID AND I.INDEX_ID = D.INDEX_ID
INNER JOIN SYS.OBJECTS O ON I.OBJECT_ID = O.OBJECT_ID
WHERE AVG_FRAGMENTATION_IN_PERCENT >= 20 AND I.NAME IS NOT NULL
AND D.PAGE_COUNT > 8 '
DECLARE @nvc_ParamDefDB NVARCHAR(200)

EXEC SP_EXECUTESQL @nvc_Qry

FETCH NEXT FROM cur_DBList INTO @vc_DBName, @in_DBID
END

--print @nvc_qry

CLOSE cur_DBList
DEALLOCATE cur_DBList
END

Conclusion

In this article we can also exclude those database in which we are not interested to perform index rebuild. Just have a look at this line;


WHERE NAME NOT IN
('master', 'tempdb', 'model', 'msdb', 'ReportServer', 'ReportServerTempDB') --Add if want to exclude any

You can add other databases too here. Replace below lines as per your need to set fragmentation level and page count. You can also put it in procedure parameters;


WHERE AVG_FRAGMENTATION_IN_PERCENT >= 20 AND I.NAME IS NOT NULL
AND D.PAGE_COUNT > 8

We have logged to times the details of indexes on which we are going to operate. First time we log it with vc_TypeDesc as “B” and after rebuilding it we again log it as “A” for before and after to compare it with appropriate batch number. Batch number is also dynamically fetched from the log table as below to keep it in sequence;


SELECT @in_BatchNo = (ISNULL(MAX(in_BatchNo),0) + 1) FROM MyDB.dbo.cmn_RebuildIndexesLog

As per your need you can modify other settings too in the procedure.
This procedure can be scheduled with job scheduler to perform the rebuild at the time when our server is not too much busy.
So after using this procedure share your comment on this.
Thanks.

Rate This
[Total: 0 Average: 0]

Leave a Comment

Your email address will not be published. Required fields are marked *


The reCAPTCHA verification period has expired. Please reload the page.

This site uses Akismet to reduce spam. Learn how your comment data is processed.