О шринке расписывать не буду, яндекс полон ответов "Для чего, зачем и как".
Готовых скриптов порционного шринка на просторах я не обнаружил, прошу не кидать тапками, если все же такие есть.
Проблемы полноценного шринка базы:
1. Непонятно, сколько времени это займет с учетом нагрузки на базу (если приходится это делать на активно используемой базе)
2. Освобождение места произойдет непосредственно при окончании шринка.
Данный скрипт обрабатывает файл базы данных в цикле, итерация которого шринкует базу до определенного размера, вычисленного из доступного места в базе с учетом заданного размера порции шринка.
Скрипт выводит информацию о прогрессе через print, оповещая о том, с какого размера база шринкуется и до какого, скорость обработки порции, прогнозируемая скорость для обработки всего доступного в базе пространства, количество Mb, оставшиеся для шринка.
Сам скрипт:
use YourBase -- Выберите базу для шринка
set NOCOUNT ON;
DECLARE @PortionInMB INT = 1000 -- Замените на нужное значение, количество МБ. освобождаемое за итерацию
DECLARE @StopSizeInMB INT = 1000 -- Количество свободного места в базе, при котором нужно прекратить шринк
DECLARE @CurrentSizeInMB INT
DECLARE @FreeSpaceInMB INT
DECLARE @CurrentDBName nvarchar(64) = DB_NAME()
DECLARE @MomentumSize INT
DECLARE @StartTime datetime2
DECLARE @EndTime datetime2
DECLARE @IterationTime INT
DECLARE @RequiredSeconds INT
SET @FreeSpaceInMB = (select convert(decimal(12,2),round((a.size-fileproperty(a.name,'SpaceUsed'))/128.000,2)) as FreeSpaceMB from dbo.sysfiles a where status & 64 = 0)
WHILE @FreeSpaceInMB > @StopSizeInMB
BEGIN
SET @CurrentSizeInMB = (select convert(decimal(12,2),round(a.size/128.000,2)) as FileSizeMB from dbo.sysfiles a where status & 64 = 0)
SET @FreeSpaceInMB = (select convert(decimal(12,2),round((a.size-fileproperty(a.name,'SpaceUsed'))/128.000,2)) as FreeSpaceMB from dbo.sysfiles a where status & 64 = 0)
SET @MomentumSize = @CurrentSizeInMB - @PortionInMB
IF @FreeSpaceInMB > @StopSizeInMB
BEGIN
SET @StartTime = CURRENT_TIMESTAMP
print CONVERT(VARCHAR(100), CURRENT_TIMESTAMP) + ': Start shrink iteration for database [' + @CurrentDBName + ']. From ' + CONVERT(VARCHAR(100), @CurrentSizeInMB)
+ ' to ' + CONVERT(VARCHAR(100), @MomentumSize) + '. Remains: ' + CONVERT(VARCHAR(100), @FreeSpaceInMB - @PortionInMB - @StopSizeInMB) + ' Mb.'
RAISERROR( '',0,1) WITH NOWAIT
DBCC SHRINKFILE (@CurrentDBName , @MomentumSize)
SET @EndTime = CURRENT_TIMESTAMP
SET @RequiredSeconds = (@FreeSpaceInMB - @StopSizeInMB) / @PortionInMB * datediff(s, @StartTime, @EndTime)
print 'Iteration successfull on ' + CONVERT(VARCHAR(100), datediff(s, @StartTime, @EndTime)) + ' sec. The required time for all iteration: '
+ CONVERT(VARCHAR(100), @RequiredSeconds) + ' sec. Completion time: ' + CONVERT(VARCHAR(100), DATEADD(s,@RequiredSeconds,CURRENT_TIMESTAMP))
RAISERROR( '',0,1) WITH NOWAIT
END
ELSE
BEGIN
BREAK;
END
END
Нюансы:
Если в вашей базе больше одной файловой группы, скрипт не сработает, необходимо прицелиться в необходимый файл, данные по которому получаются в скрипте из dbo.sysfiles, можно либо отобрать топ по размеру, отобрать по имени и т.д.
RAISERROR использован для оперативного получения информации, переданной в print.