Параметрический -каждый параметр можно подстроить под себя
Успешно работает на нескольких серверах
Протестирован на MS SQL 2019
Внимание!
Скрипт обрабатывает только часто используемые индексы
чтобы убрать тормоза на других. редко используемых объектах - настройте по ним пртнудительно обновление статистики ( например для отчета по производительности)
Скрипт адаптивной обработки индекса, если индекс используется активно (параметр) и фрагментирован (параметр) или статистика неактуальна (параметр), то выполняет команды по обслуживанию индекса.
Каждая команда выводит в комментарий - почему она выполнилась.
Также можно как показать, что будет делать, так и выполнить сразу
DECLARE @OnlyShow INT = 0; --1 = только показать, 0 = еще и выполнить
DECLARE @threshold_modification_pct INT = 1; -- Сколько процентов считать важными изменениями. Таблицы большие и 1% на миллионах –уже 10 000…
DECLARE @minrows INT = 100; -- брать если строк больше в индексе
DECLARE @minused INT = 1000; -- если использовали больше чем 1000 раз
DECLARE @minSteps INT = 10; -- не берем индкесы если они с точки статистики бестолковые
DECLARE @maxRows INT = 100000; -- Или количество строк меньше чем то игнор предыдущее условие
DECLARE @FragsLevel INT = 5; -- дефраг только если больше
DECLARE @FragsLevelRebuild INT = 30; -- rebuild если фрагментация больше
DECLARE @EnterpriseEdition INT = 0; --case when SERVERPROPERTY ('edition')='Enterprise Edition (64-bit)' then 1 else 0 end
---- служебные переменные
DECLARE @command NVARCHAR(500);
DECLARE @TableName SYSNAME;
DECLARE @IndexName SYSNAME;
DECLARE @modification_counter NVARCHAR(200);
DECLARE @Table_rows NVARCHAR(200);
DECLARE @Table_used NVARCHAR(200);
DECLARE @Steps NVARCHAR(200);
DECLARE @avg_fragmentation_in_percent NUMERIC;
DECLARE @levelForDefrag NUMERIC;
DECLARE @Frag NVARCHAR(200);
DECLARE @Variant NVARCHAR(20);
DECLARE indexes CURSOR FOR
SELECT
obj.NAME [table],
stat.NAME [index],
modification_counter [modification_counter],
sp.rows [Table_rows],
user_seeks + user_scans + user_lookups AS Table_used,
sp.steps Steps,
ips.avg_fragmentation_in_percent AS avg_fragmentation_in_percent ,
ips.avg_fragmentation_in_percent AS Frag,
sp.rows * @threshold_modification_pct / 100 AS levelForDefrag,
CASE
WHEN modification_counter >
sp.rows * @threshold_modification_pct / 100
THEN
N'UPDATE STATISTICS'
WHEN ips.avg_fragmentation_in_percent > @FragsLevelRebuild THEN
'REBUILD'
ELSE 'REORGANIZE'
END AS Variant
FROM sys.objects AS obj
INNER JOIN sys.stats AS stat
ON stat.object_id = obj.object_id
CROSS apply sys.Dm_db_stats_properties(stat.object_id, stat.stats_id)
AS
sp
LEFT JOIN (SELECT *
FROM sys.Dm_db_index_physical_stats(Db_id(), NULL, NULL,
NULL, NULL
)) AS ips
ON stat.object_id = ips.object_id
AND stat.stats_id = ips.index_id
INNER JOIN sys.dm_db_index_usage_stats AS s
ON stat.object_id = s.object_id
AND stat.stats_id = s.index_id
WHERE s.database_id = Db_id()
AND obj.type = 'U'
AND stat.NAME NOT LIKE ( '_WA%' )
AND ips.alloc_unit_type_desc = 'IN_ROW_DATA'
AND @minrows < sp.rows
AND ( user_seeks + user_scans + user_lookups ) >= @minused
AND ( ips.avg_fragmentation_in_percent > @FragsLevel
OR ( modification_counter >
sp.rows * @threshold_modification_pct / 100 )
)
AND ( sp.steps > @minSteps
OR @maxRows > sp.rows )
--ORDER BY stat.NAME
; ---(user_seeks + user_scans + user_lookups) desc;
OPEN indexes;
WHILE ( 1 = 1 )
BEGIN
FETCH next FROM indexes INTO @TableName, @IndexName, @modification_counter ,
@Table_rows, @Table_used, @Steps, @avg_fragmentation_in_percent, @Frag,
@levelForDefrag,@Variant;
IF @@FETCH_STATUS < 0
BREAK;
IF @levelForDefrag < @modification_counter
SET @command = N'UPDATE STATISTICS dbo.' + @TableName + ' '
+ @IndexName
+ N' WITH FULLSCAN, MAXDOP=8; -- Steps '
+ @Steps + ', used ' + @Table_used + ', changed '
+ @modification_counter + ' rows of '
+ @Table_rows;
ELSE
SET @command =
CASE
WHEN @variant='REBUILD'
THEN
''
ELSE
N'ALTER INDEX ' + @IndexName + ' on dbo.' + @TableName+ ' SET (ALLOW_PAGE_LOCKS = ON, ALLOW_ROW_LOCKS = ON);'
END
+ N'ALTER INDEX ' + @IndexName + ' on dbo.' + @TableName
+ CASE
WHEN @variant='REBUILD'
THEN
' REBUILD WITH (SORT_IN_TEMPDB = ON, maxdop=8' + case When @EnterpriseEdition=1 then ', online=on' else '' end +')'
ELSE
' REORGANIZE '
END
+ CASE
WHEN @variant='REBUILD'
THEN
''
ELSE
N'; ALTER INDEX ' + @IndexName + ' on dbo.' + @TableName+ ' SET (ALLOW_PAGE_LOCKS = OFF, ALLOW_ROW_LOCKS = ON)'
END
+ '; --- frag = ' + @Frag + '%' + ', rows = ' + @Table_rows;
PRINT N'Executed: ' + @command;
IF @OnlyShow = 1
CONTINUE;
EXEC (@command);
END;
CLOSE indexes;
DEALLOCATE indexes;