为了有针对性的重建索引,提高数据库的效能,同时避免因全部重建造成服务器的Loading,我们内部有开发一个存储过程脚本,逻辑大概如下:
首先遍历该实例中所有数据库中所有table中的index,并将Fragment保存到临时表中,这当中用到了一个系统存储过程:sp_MSforeachdb 可以实现对所有数据库的循环遍历;然后对临时表中的数据进行处理,如果
碎片介于20%--40%,则对索引进行重新组织,大于40%则进行重建。
1 USE [msdb] 2 GO 3 /****** Object: StoredProcedure [dbo].[IndexMaintain] Script Date: 09/14/2012 17:59:33 ******/ 4 SET ANSI_NULLS ON 5 GO 6 SET QUOTED_IDENTIFIER ON 7 GO 8 -- ============================================= 9 -- Author 10 -- Create date: 2012/01/17 11 -- Description: IndexMaintain 12 -- ============================================== 13 ALTER procedure [dbo].[IndexMaintain] 14 as 15 SET NOCOUNT on 16 17 BEGIN TRY 18 declare @EXCEPTION VARCHAR(MAX) 19 declare @MailSubject NVARCHAR(255) 20 declare @DBName NVARCHAR(255) 21 declare @TableName NVARCHAR(255) 22 declare @SchemaName NVARCHAR(255) 23 declare @IndexName NVARCHAR(255) 24 declare @avg_fragmentation_in_percent_old DECIMAL(18,3) 25 declare @avg_page_space_used_in_percent_old DECIMAL(18,3) 26 declare @avg_fragmentation_in_percent_new DECIMAL(18,3) 27 declare @avg_page_space_used_in_percent_new DECIMAL(18,3) 28 29 declare @Defrag NVARCHAR(max) 30 declare @Sql NVARCHAR(max) 31 declare @ParmDefinition nvarchar(500) 32 set @EXCEPTION='' 33 34 --删除#Frag 35 if exists(select * from sys.objects where object_id=object_id(N'#Frag')) 36 drop table #Frag 37 38 --定义临时表#Frag保存index Fragment 39 create table #Frag( 40 DBname NVARCHAR(255), 41 TableName NVARCHAR(255), 42 SchemaName NVARCHAR(255), 43 IndexName NVARCHAR(255), 44 AvgFragment DECIMAL(18,3), 45 avg_page_space_used DECIMAL(18,3) 46 ) 47 48 --遍历DB中所有table上的index,并将Fragment保存到临时表#Frag中. 49 exec sp_MSforeachdb @command1= 'insert into #Frag(DBname, TableName,SchemaName,IndexName,AvgFragment,avg_page_space_used) 50 select ''[?]'' AS DBName, t.Name AS TableName, sc.Name AS SchemaName, i.name AS IndexName, s.avg_fragmentation_in_percent, s.avg_page_space_used_in_percent 51 from [?].sys.dm_db_index_physical_stats(DB_ID(''?''),NULL,NULL,NULL,''Sampled'') AS s 52 join [?].sys.indexes i on s.Object_Id=i.Object_id and s.Index_id=i.Index_id 53 join [?].sys.tables t on i.Object_id=t.Object_ID 54 join [?].sys.schemas sc on t.schema_id=sc.SCHEMA_ID 55 where s.avg_fragmentation_in_percent >20 and t.type=''U'' and s.page_count>8 and i.allow_page_locks=1 and i.allow_row_locks=1 56 order by TableName,IndexName ' 57 58 --定义CURSOR遍历临时表#Frag,根据Fragment大小采取不同的方案维护index. 59 declare cList CURSOR for 60 select * from #Frag 61 open cList 62 fetch next from cList into @DBName,@TableName,@SchemaName,@IndexName,@avg_fragmentation_in_percent_old,@avg_page_space_used_in_percent_old 63 while @@FETCH_STATUS=0 64 begin 65 set @TableName ='['+ @TableName +']' 66 --Fragment between 20.0 and 40.0 ,使用 Alter INDEX reorganize整理碎片 67 if @avg_fragmentation_in_percent_old between 20.0 and 40.0 AND @DBName <>'[TOPCOA]' 68 begin 69 --整理碎片 70 set @Defrag=N'Alter INDEX '+''+@IndexName+' on '+@DBName+'.'+@SchemaName+'.'+@TableName+' reorganize' 71 exec sp_executesql @Defrag 72 73 --获取index被整理后的碎片比例 74 set @Sql=N'USE '+@DBName+'; select @avg_fragmentation_in_percent_new_temp=s.avg_fragmentation_in_percent,@avg_page_space_used_in_percent_new_temp= s.avg_page_space_used_in_percent 75 from '+@DBName+'.sys.indexes i 76 inner join '+@DBName+'.sys.dm_db_index_physical_stats(db_id(replace(replace('''+@DBName+''',''['',''''),'']'','''')), object_id('''+@TableName+''''+'),null,null,''sampled'') as s on i.index_id=s.index_id 77 where i.object_id=object_id('''+@TableName+''''+')and i.name='''+@IndexName+'''' 78 set @ParmDefinition=N'@avg_fragmentation_in_percent_new_temp DECIMAL(18,3) output,@avg_page_space_used_in_percent_new_temp DECIMAL(18,3) output' 79 exec sp_executesql @Sql,@ParmDefinition ,@avg_fragmentation_in_percent_new_temp=@avg_fragmentation_in_percent_new output, @avg_page_space_used_in_percent_new_temp=@avg_page_space_used_in_percent_new output 80 81 --write log 82 insert [dbo].IndexDefrag values(@DBName,@TableName,@SchemaName,@IndexName,getdate(),@avg_fragmentation_in_percent_old,@avg_page_space_used_in_percent_old,@avg_fragmentation_in_percent_new,@avg_page_space_used_in_percent_new,'0') 83 end 84 --Fragment大于40.0 ,使用 Alter INDEX rebuild整理碎片 85 else if @avg_fragmentation_in_percent_old >40.0 AND @DBName <>'[TOPCOA]' 86 begin 87 --整理碎片 88 set @Defrag=N'Alter INDEX '+''+@IndexName+' on '+@DBName+'.'+@SchemaName+'.'+@TableName+' rebuild' 89 exec sp_executesql @Defrag 90 91 --获取index被整理后的碎片比例 92 set @Sql=N'USE '+@DBName+';select @avg_fragmentation_in_percent_new_temp=s.avg_fragmentation_in_percent,@avg_page_space_used_in_percent_new_temp= s.avg_page_space_used_in_percent 93 from '+@DBName+'.sys.indexes i 94 inner join '+@DBName+'.sys.dm_db_index_physical_stats(db_id(replace(replace('''+@DBName+''',''['',''''),'']'','''')), object_id('''+@TableName+''''+'),null,null,''sampled'') as s on i.index_id=s.index_id 95 where i.object_id=object_id('''+@TableName+''''+')and i.name='''+@IndexName+'''' 96 set @ParmDefinition=N'@avg_fragmentation_in_percent_new_temp DECIMAL(18,3) output,@avg_page_space_used_in_percent_new_temp DECIMAL(18,3) output' 97 exec sp_executesql @Sql,@ParmDefinition ,@avg_fragmentation_in_percent_new_temp=@avg_fragmentation_in_percent_new output, @avg_page_space_used_in_percent_new_temp=@avg_page_space_used_in_percent_new output 98 99 --write log 100 insert [dbo].IndexDefrag values(@DBName,@TableName,@SchemaName,@IndexName,getdate(),@avg_fragmentation_in_percent_old,@avg_page_space_used_in_percent_old,@avg_fragmentation_in_percent_new,@avg_page_space_used_in_percent_new,'1') 101 end 102 fetch next from cList into @DBName,@TableName,@SchemaName,@IndexName,@avg_fragmentation_in_percent_old,@avg_page_space_used_in_percent_old 103 end 104 close cList 105 deallocate cList 106 END TRY 107 BEGIN CATCH 108 SET @EXCEPTION = ERROR_MESSAGE() 109 END CATCH 110 111 IF @EXCEPTION<>'' 112 BEGIN 113 SET @MailSubject='[Important]DB Index Maintainence failed from ' + @@SERVERNAME 114 EXEC msdb.dbo.sp_send_dbmail 115 @profile_name = 'mail', 116 @recipients = '[email protected]', 117 @body = @EXCEPTION, 118 @subject = @MailSubject 119 120 END 121