Microsoft SQL Server database backup script with full recovery model

In continuation of my previous article on archiving MSSQL databases and the negative response due to the lack of the possibility of archiving transaction logs, I now work in a company where it was necessary to automate this moment for databases, including those with a full recovery model.

The script works universally for databases with different recovery models; at the beginning of the script, settings have been added for relatively flexible scheduling. The script can be installed with SQL Agent and a convenient interval (for example, I have 1 time per hour), the first run in a day will check whether or not a full or differential copy should be created today and then backups will be created for a database with a full recovery model during the day transaction log log.

-- пути до бэкапов 
declare @FullPath varchar(500) = 'D:\SQLBackup'
declare @DiffPath varchar(500) = 'D:\SQLBackup'
declare @LogPath varchar(500) = 'D:\SQLBackup'

-- архивируемые и исключенные из архивации БД 
declare @IncludeBase varchar(500) = 'master, msdb' -- если не пусто, то только эти минус исключенные, если пусто то все минус исключенные
declare @ExcludeBase varchar(500) = 'tempdb'


-- настроки для полного бэкапа
-- минимум хранить дней
declare @MinFullDay int = 181
-- выполнять каждые дней
declare @MinFullExecDay int = 7


-- настроки для разностного бэкапа
-- минимум хранить дней
declare @MinDiffDay int = 181
-- выполнять каждые дней
declare @MinDiffExecDay int = 1

-- настроки для бэкапа журнала
-- минимум хранить дней
declare @MinLogDay int = 35



-- как часто создавать master (дни)
declare @MinDayMaster int = 1

-- сколько хранить информацию об архивах в msdb (дни)
--declare @DayHistory int = @MinFullDay + 30


-- включим сжатие 
EXEC sp_configure 'show advanced options', 1;  EXEC sp_configure 'backup compression default', 1; RECONFIGURE WITH OVERRIDE;
-- включим xp_cmdshell (нужно для удаления старых бэкапов)
EXEC sp_configure 'show advanced options', 1;  EXEC sp_configure 'xp_cmdshell', 1;  RECONFIGURE WITH OVERRIDE;  

declare @tempcmd varchar(500) =''
declare @tempname varchar(500) =''

-- создание путей
set @tempcmd= 'md '+@FullPath
exec xp_cmdshell @tempcmd, no_output
set @tempcmd= 'md '+@DiffPath
exec xp_cmdshell @tempcmd, no_output
set @tempcmd= 'md '+@LogPath
exec xp_cmdshell @tempcmd, no_output



-- определяем список БД для архивации
declare @BaseListIncl table (name varchar(200))
declare @BaseListExcl table (name varchar(200))

if @IncludeBase="" 
		insert into @BaseListIncl select name from sys.databases where state_desc="ONLINE" 
	else
		while len(@IncludeBase)>0
		begin
			if CHARINDEX (',',@IncludeBase)>0
				begin
					insert into @BaseListIncl select name from sys.databases where state_desc="ONLINE" and name = SUBSTRING(@IncludeBase,1, CHARINDEX (',',@IncludeBase)-1)
					set @IncludeBase=LTRIM(RTRIM(SUBSTRING(@IncludeBase,CHARINDEX (',',@IncludeBase)+1, LEN(@IncludeBase))))
				end
			else
				begin
					insert into @BaseListIncl select name from sys.databases where state_desc="ONLINE" and name = @IncludeBase
					set @IncludeBase=""
				end
		 end

if @ExcludeBase<>'' 
		while len(@ExcludeBase)>0
		begin
			if CHARINDEX (',',@ExcludeBase)>0
				begin
					insert into @BaseListExcl select name from sys.databases where state_desc="ONLINE" and name = SUBSTRING(@ExcludeBase,1, CHARINDEX (',',@ExcludeBase)-1)
					set @ExcludeBase=LTRIM(RTRIM(SUBSTRING(@ExcludeBase,CHARINDEX (',',@ExcludeBase)+1, LEN(@ExcludeBase))))
				end
			else
				begin
					insert into @BaseListExcl select name from sys.databases where state_desc="ONLINE" and name = @ExcludeBase
					set @ExcludeBase=""
				end
		 end

-- итоговый список БД для бэкапа
delete from @BaseListIncl 
	where name in (select name from @BaseListExcl)

declare BaseList cursor for
	select name from @BaseListIncl 

declare @BaseName varchar(500) =''
declare @BackupType varchar(10) = ''

open BaseList
fetch next from BaseList into @BaseName  

while @@FETCH_STATUS = 0  
	begin  
	    
	-- получаем тип бэкапа, который надо сделать (для мастера специальные условия, он не может ни каких кроме FULL)
	SET @BackupType =   
	    ( CASE  
			WHEN @BaseName="master" AND
				(EXISTS (SELECT * FROM msdb.dbo.backupset s
					INNER JOIN msdb.dbo.backupmediafamily mf ON s.media_set_id = mf.media_set_id
					WHERE s.database_name=@BaseName and s.TYPE='D' and DATEDIFF(day, CAST(s.backup_finish_date AS DATE), CAST(GETDATE() AS DATE)) < @MinDayMaster))
				THEN ''
			WHEN @BaseName="master" AND
				(NOT EXISTS (SELECT * FROM msdb.dbo.backupset s
					INNER JOIN msdb.dbo.backupmediafamily mf ON s.media_set_id = mf.media_set_id
					WHERE s.database_name=@BaseName and s.TYPE='D' and DATEDIFF(day, CAST(s.backup_finish_date AS DATE), CAST(GETDATE() AS DATE)) < @MinDayMaster))
				THEN 'FULL'
			WHEN (NOT EXISTS (SELECT * FROM msdb.dbo.backupset s
				INNER JOIN msdb.dbo.backupmediafamily mf ON s.media_set_id = mf.media_set_id
				WHERE s.database_name=@BaseName and s.TYPE='D' and DATEDIFF(day, CAST(s.backup_finish_date AS DATE), CAST(GETDATE() AS DATE)) < @MinFullExecDay)) 
				THEN 'FULL'
			WHEN (NOT EXISTS (SELECT * FROM msdb.dbo.backupset s
				INNER JOIN msdb.dbo.backupmediafamily mf ON s.media_set_id = mf.media_set_id
				WHERE s.database_name=@BaseName and s.TYPE IN ('I','D') and DATEDIFF(day, CAST(s.backup_finish_date AS DATE), CAST(GETDATE() AS DATE)) < @MinDiffExecDay)) 
				THEN 'DIFF'
			WHEN (EXISTS (select * from sys.databases as a where a.recovery_model_desc="FULL" and a.name = @BaseName)) 
				THEN 'LOG'
			ELSE ''
		END  
		) 


	IF @BackupType <> ''
	begin


	--создаем папку для БД
	SET @tempcmd = 
		(CASE
			WHEN @BackupType="FULL" OR @BaseName="master" THEN 'md '+@FullPath+'\'+@BaseName
			WHEN @BackupType="DIFF" THEN 'md '+@DiffPath+'\'+@BaseName
			ELSE 'md '+@LogPath+'\'+@BaseName
		END)
		exec xp_cmdshell @tempcmd, no_output


		
		if @BackupType="FULL"
			begin
				-- full backup
				set @tempname = @FullPath+'\'+@BaseName+'\'+@BaseName+'_'+CONVERT(varchar(8), GETDATE(), 112)+ '-' + REPLACE(CONVERT(varchar, GETDATE(),114),':','') +'_FULL.BAK'
				backup database @BaseName to disk = @tempname
			end
		else if @BackupType="DIFF"
			begin
				-- diff backup
				set @tempname = @DiffPath+'\'+@BaseName+'\'+@BaseName+'_'+CONVERT(varchar(8), GETDATE(), 112)+ '-' + REPLACE(CONVERT(varchar, GETDATE(),114),':','') +'_DIFF.BAK'
				backup database @BaseName to disk =	@tempname with differential
			end
		else 
			begin
				-- log backup
				set @tempname = @LogPath+'\'+@BaseName+'\'+@BaseName+'_'+CONVERT(varchar(8), GETDATE(), 112)+ '-' + REPLACE(CONVERT(varchar, GETDATE(),114),':','') +'_LOG.TRN'
				BACKUP LOG @BaseName to disk =	@tempname 
			end


		-- удаляем истекшие бэкапы
		declare @delpath varchar(500)=''
		declare delbackup cursor for

			SELECT mf.physical_device_name
				FROM   msdb.dbo.backupset s
				INNER JOIN msdb.dbo.backupmediafamily mf ON s.media_set_id = mf.media_set_id
			WHERE s.database_name=@BaseName and s.TYPE='D'
			and DATEDIFF(day, CAST(s.backup_finish_date AS DATE), CAST(GETDATE() AS DATE)) > @MinFullDay
			and is_copy_only = 0

		union all
			SELECT mf.physical_device_name
				FROM   msdb.dbo.backupset s
				INNER JOIN msdb.dbo.backupmediafamily mf ON s.media_set_id = mf.media_set_id
			WHERE s.database_name=@BaseName and s.TYPE='I'
			and DATEDIFF(day, CAST(s.backup_finish_date AS DATE), CAST(GETDATE() AS DATE)) > @MinDiffDay
			and is_copy_only = 0

		union all
			SELECT mf.physical_device_name
				FROM   msdb.dbo.backupset s
				INNER JOIN msdb.dbo.backupmediafamily mf ON s.media_set_id = mf.media_set_id
			WHERE s.database_name=@BaseName and s.TYPE='L'
			and DATEDIFF(day, CAST(s.backup_finish_date AS DATE), CAST(GETDATE() AS DATE)) > @MinLogDay
			and is_copy_only = 0



		 open delbackup
		 fetch next from delbackup into @delpath  
		 while @@FETCH_STATUS = 0 
			begin
				set @tempcmd= 'del /f /q '+QUOTENAME(@delpath,'"')
				exec xp_cmdshell @tempcmd, no_output
			  fetch next from delbackup into @delpath
			end
		 close delbackup
		 deallocate delbackup

		end

		fetch next from BaseList into @BaseName  
    end
  
close BaseList  
deallocate BaseList 


-- чистим в MSDB информацию о старых архивах
--declare @oldest DATETIME 
--SET @oldest = DATEADD(DAY, -@DayHistory, GETDATE())

--EXEC msdb.dbo.sp_delete_backuphistory @oldest_date = @oldest

I will also add a script for creating “long-term storage” backups. They are created with the ONLY_COPY option and do not participate in the general recovery chain, I usually use it once or twice a month to make a full copy or as needed

-- ========== СОЗДАЕМ ТОЛЬКО ПОЛНЫЕ БЭКАПЫ С ОПЦИЕЙ ONLY_COPY (не учавствуют в общей цепочке)

-- пути до бэкапов (для типовых)
declare @Path varchar(500) = 'I:\SQLBackupOnlyCopy'

-- архивируемые и исключенные из архивации БД 
declare @IncludeBase varchar(500) = 'buh, zup' -- если не пусто, то только эти минус исключенные, если пусто то все минус исключенные
declare @ExcludeBase varchar(500) = 'tempdb'


-- включим сжатие 
EXEC sp_configure 'show advanced options', 1;  EXEC sp_configure 'backup compression default', 1; RECONFIGURE WITH OVERRIDE;
-- включим xp_cmdshell (нужно для удаления старых бэкапов)
EXEC sp_configure 'show advanced options', 1;  EXEC sp_configure 'xp_cmdshell', 1;  RECONFIGURE WITH OVERRIDE;  

declare @tempcmd varchar(500) =''
declare @tempname varchar(500) =''

-- создание путей
set @tempcmd= 'md '+@Path
exec xp_cmdshell @tempcmd, no_output

-- определяем список БД для архивации
declare @BaseListIncl table (name varchar(200))
declare @BaseListExcl table (name varchar(200))

if @IncludeBase="" 
		insert into @BaseListIncl select name from sys.databases where state_desc="ONLINE" 
	else
		while len(@IncludeBase)>0
		begin
			if CHARINDEX (',',@IncludeBase)>0
				begin
					insert into @BaseListIncl select name from sys.databases where state_desc="ONLINE" and name = SUBSTRING(@IncludeBase,1, CHARINDEX (',',@IncludeBase)-1)
					set @IncludeBase=LTRIM(RTRIM(SUBSTRING(@IncludeBase,CHARINDEX (',',@IncludeBase)+1, LEN(@IncludeBase))))
				end
			else
				begin
					insert into @BaseListIncl select name from sys.databases where state_desc="ONLINE" and name = @IncludeBase
					set @IncludeBase=""
				end
		 end

if @ExcludeBase<>'' 
		while len(@ExcludeBase)>0
		begin
			if CHARINDEX (',',@ExcludeBase)>0
				begin
					insert into @BaseListExcl select name from sys.databases where state_desc="ONLINE" and name = SUBSTRING(@ExcludeBase,1, CHARINDEX (',',@ExcludeBase)-1)
					set @ExcludeBase=LTRIM(RTRIM(SUBSTRING(@ExcludeBase,CHARINDEX (',',@ExcludeBase)+1, LEN(@ExcludeBase))))
				end
			else
				begin
					insert into @BaseListExcl select name from sys.databases where state_desc="ONLINE" and name = @ExcludeBase
					set @ExcludeBase=""
				end
		 end

-- итоговый список БД для бэкапа
delete from @BaseListIncl 
	where name in (select name from @BaseListExcl)

declare BaseList cursor for
	select name from @BaseListIncl 

declare @BaseName varchar(500) =''
declare @BackupType varchar(10) = ''

open BaseList
fetch next from BaseList into @BaseName  

while @@FETCH_STATUS = 0  
	begin  
	    
	--создаем папку для БД
	SET @tempcmd = 'md '+@Path+'\'+@BaseName
	exec xp_cmdshell @tempcmd, no_output

	--создаем архив
	set @tempname = @Path+'\'+@BaseName+'\'+@BaseName+'_'+CONVERT(varchar(8), GETDATE(), 112)+ '-' + REPLACE(CONVERT(varchar, GETDATE(),114),':','') +'_FULL_CopyOnly.BAK'
	backup database @BaseName to disk = @tempname WITH COPY_ONLY


	fetch next from BaseList into @BaseName  

    end
  
close BaseList  
deallocate BaseList 

Similar Posts

Leave a Reply