[Prior Post] [Next Post]
Getting backups scheduled appropriately tends to be a slow process with a lot of keying and mouse clicks. The process can be simplified by the use of a couple of scripts that will backup every database on your SQL Server (including databases added to your SQL Server later!)
There are three types of backups:
The pattern that I coded below is to:
If this pattern is not satisfactory for you needs, feel free to leave a comment on what you need and I will provide it in a follow up post.
I am providing you the option of doing two types of backups:
Roll-over backups allow the latest version to be recovered only, with the advantage that you reuse disk space. Weekly-distinct backups have the disadvantage of consuming more and more disk space, but they allow you to recover to any point in the past, as long as you have a backup for it. The table below illustrates the backup files you will see for database “ISV”.
Rollover Backup
The choice often depends on any potential need to rollback to a point of time. Typical needs driving weekly distinct backups are:
The scripts build on each other, so I will describe them from the bottom up.
The scripts are adaptive. If you add a database, or the server is down when a backup should happen, the scripts adapt to these conditions.
This script takes four parameters that you may wish to change the default values for:
There are many options available as shown in help. I tried to keep things simple but I decided to put compression on because it detects disk write errors.
CREATE PROC [dbo].[SQLISV_DoBackup] @DBName Sysname=null, @BackupPath nvarchar(255)='P:\Backups\', @MinLogSize int =1, @Overwrite bit = 1, @CheckDb tinyint = 0 -- 0: Nocheck -- 1:Check on FULL -- 2: Check on FULL and Differertial AS SET NOCOUNT ON DECLARE @BackupFileName nvarchar(max), @WeekDate Date, @Cmd Nvarchar(max), @WithOptions Nvarchar(max), @DayOffset int, @Hour int, @SimpleRecoverModel bit = 0, @FullBackupHour int=1, @FullBackupDayOffset int=1 IF @DBName is Null SET @DBName=DB_Name() IF @DBName in ('tempdb') BEGIN Print 'TempDb is not backed up' RETURN END IF (SELECT DATABASEPROPERTYEX(@DBName,'Recovery'))='SIMPLE' SET @SimpleRecoverModel =1 SET @Hour=DatePart(hh,GetDate()) SET @WeekDate=GetDate() SET @DayOffset=DatePart(dw,@WeekDate) SET @WeekDate=DateAdd(dy,-@DayOffset,@WeekDate) IF @Overwrite = 0 BEGIN SET @BackupFileName= @BackupPath + REPLACE(@@SERVERNAME,'\','-')+' ' + @DBName +' ' + Convert(varchar(8),@WeekDate, 112) +'.bak' SET @WithOptions ='COMPRESSION' END ELSE BEGIN SET @BackupFileName= @BackupPath + REPLACE(@@SERVERNAME,'\','-')+' ' + @DBName +' Backup' +'.bak' SET @WithOptions ='COMPRESSION, RETAINDAYS=21' END -- Borrowed from early post, we want to determine if log is above threshold IF NOT EXISTS(SELECT name from sysobjects where name='SQLISV_LogSpace' and type='P') BEGIN SET @CMD='CREATE Proc SQLISV_LogSpace AS DBCC SQLPERF(logspace)' print @Cmd EXEC (@Cmd) END CREATE TABLE #LogSpace ( DBName sysname, logSize decimal(18,5), logUsed decimal(18,5), status int ) INSERT INTO #LogSpace exec SQLISV_LogSpace -- Always do a full backup on a new file IF NOT EXISTS (SELECT 1 FROM msdb.dbo.backupset JOIN msdb.dbo.backupmediafamily ON backupset.media_set_id = backupmediafamily.media_set_id AND is_copy_only = 0 -- Is NOT COPY ONLY WHERE database_name = @DBName AND [type]='D' -- Is FULL BACKUP AND @BackupFileName=physical_device_name -- to Right Location AND DateDiff(hh,backup_start_date,GetDate()) < 7 * 24 +1 -- Full backups must occur at least once every 7 days. ) OR -- At specified time and day of week (@FullBackupHour=@Hour AND @FullBackupDayOffset = @DayOffset) BEGIN IF @CheckDB > 0 BEGIN SET @CMD='DBCC CHECKDB (['+@DBName + ']) WITH NO_INFOMSGS' print @Cmd EXEC (@CMD) END SET @CMD='BACKUP DATABASE ['+@DBName + '] TO DISK = ''' + @BackupFileName+''' WITH '+@WithOptions print @Cmd EXEC (@CMD) IF @DBName in ('Master','model','msdb') OR @SimpleRecoverModel =1 -- Can't backup LOG on SIMPLE return SET @CMD='BACKUP LOG ['+@DBName + '] TO DISK = ''' + @BackupFileName+''' WITH '+@WithOptions Print @Cmd EXEC (@CMD) RETURN END IF @DBName in ('Master','model','msdb') BEGIN Print @DBName Print 'Master,model,msdb has been backedup this week' RETURN END -- DIFFERENTIAL IS DONE AT 1 AM (or next opportunity if machine is down at 1AM) IF not exists(SELECT 1 FROM msdb.dbo.backupset JOIN msdb.dbo.backupmediafamily ON backupset.media_set_id = backupmediafamily.media_set_id AND is_copy_only = 0 WHERE [type]='I' -- Differential Backup AND database_name = @DBName AND @BackupFileName=physical_device_name -- No more than one every 12 hrs AND DateDiff(hh,backup_start_date,GetDate()) < 12 -- Should happen at 1AM but if machine offline, then next time AND @hour BETWEEN 1 AND 7 -- Between 1AM and 7AM ) BEGIN IF @CheckDB > 1 BEGIN SET @CMD='DBCC CHECKDB (['+@DBName + ']) WITH NO_INFOMSGS' print @Cmd EXEC (@CMD) END SET @CMD='BACKUP DATABASE ['+@DBName + '] TO DISK = ''' + @BackupFileName+''' WITH DIFFERENTIAL, ' +@WithOptions Print @Cmd EXEC (@CMD) RETURN END ELSE IF EXISTS(SELECT 1 FROM #LogSpace WHERE Cast(logSize* logUsed/100 as decimal(18,2)) > @MinLogSize AND DBName=@DBName) BEGIN IF not exists(SELECT 1 FROM msdb.dbo.backupset JOIN msdb.dbo.backupmediafamily ON backupset.media_set_id = backupmediafamily.media_set_id AND is_copy_only = 0 WHERE [type]='L' AND DateDiff(mm,GetDate(),backup_start_date) > 50 AND database_name = @DBName AND @BackupFileName=physical_device_name) AND @SimpleRecoverModel =0 -- Can't backup LOG on SIMPLE BEGIN SET @CMD='BACKUP LOG ['+@DBName + '] TO DISK = ''' + @BackupFileName+''' WITH '+@WithOptions Print @Cmd EXEC (@CMD) RETURN END ELSE BEGIN Print 'Log is too small - delaying' RETURN END END Print 'Nothing happened' GO CREATE PROC [dbo].[SQLISV_BackupAllDatabases] @BackupPath nvarchar(255)='P:\Backups\', @MinLogSize int =1000 AS DECLARE @DBID int=1, @DBName Sysname DECLARE @DBTable table (DBID int identity(1,1),DBName sysname) INSERT INTO @DBTable (DBName) SELECT Name FROM sys.databases WHILE 1=1 BEGIN SELECT @DBName=DBName FROM @DBTable WHERE @DBID=DBID IF @@ROWCOUNT > 0 BEGIN TRY SET @DBID +=1 EXEC [SQLTools].dbo.[SQLISV_DoBackup] @DBName=@DBName, @BackupPath=@BackupPath, @MinLogSize=@MinLogSize END TRY BEGIN CATCH PRINT ERROR_MESSAGE() END CATCH ELSE BREAK END GO
When this executes, I print out the backup TSQL commands executed to facilitate debugging.
This script walks the databases and calls the above script for each one. I assumed @Overwrite is defaulted to the appropriate value above and retained only two of the parameters.
USE [SQLTools] GO CREATE PROC [dbo].[SQLISV_BackupAllDatabases] @BackupPath nvarchar(255)='P:\Backups\', @MinLogSize int =1000 AS DECLARE @DBID int=1, @DBName Sysname DECLARE @DBTable table (DBID int identity(1,1),DBName sysname) INSERT INTO @DBTable (DBName) SELECT Name FROM sys.databases WHILE 1=1 BEGIN SELECT @DBName=DBName FROM @DBTable WHERE @DBID=DBID IF @@ROWCOUNT > 0 BEGIN TRY SET @DBID +=1 EXEC [SQLTools].dbo.[SQLISV_DoBackup] @DBName=@DBName, @BackupPath=@BackupPath, @MinLogSize=@MinLogSize END TRY BEGIN CATCH PRINT ERROR_MESSAGE() END CATCH ELSE BREAK END GO
I would suggest changing the Catch block to send a notification out, for example an email.
The script below will add jobs to call the above script if they do not exist and replace jobs if they do exist.
USE [SQLTools] GO CREATE PROC [dbo].[SQLISV_ScheduleBackupAllDatabases] ASBEGIN IF Exists(SELECT 1 FROM msdb.dbo.sysjobs WHERE name='SQLISV_BackupAllDatabases') Exec msdb.dbo.sp_delete_job @job_name='SQLISV_BackupAllDatabases' Exec msdb.dbo.sp_add_job @job_name='SQLISV_BackupAllDatabases', @notify_level_eventlog=3 Exec msdb.dbo.sp_add_jobstep @job_name='SQLISV_BackupAllDatabases', @Step_Name='SQLISV_BackupAllDatabases_1', @Command='EXEC [SQLTools].[dbo].[SQLISV_BackupAllDatabases]' IF Exists(Select 1 from msdb.dbo.sysschedules WHERE name='SQLISV_BackupAllDatabases') EXEC msdb.dbo.sp_delete_schedule @schedule_name = 'SQLISV_BackupAllDatabases' Exec msdb.dbo.sp_add_schedule @schedule_name = 'SQLISV_BackupAllDatabases', @freq_type = 4, --Daily @freq_interval=1, @freq_subday_type=0x8, -- Hour @freq_subday_interval=1 EXEC msdb.dbo.sp_attach_schedule @job_name = N'SQLISV_BackupAllDatabases', @schedule_name = N'SQLISV_BackupAllDatabases' ; ENDGOEXEC [dbo].[SQLISV_ScheduleBackupAllDatabases]
In the SQLISV_ScheduleBackupAllDatabases code above, I specified @notify_level_eventlog=3. This setting means that successful and unsuccessful backups are recorded in the Event Log as shown below.
Some of the backups failed, so I needed to investigate why. I can manually execute the backup and attempt to identify what the problem is. An example of this process is shown below.
The code above tests for this condition and omits the log backups for databases with a SIMPLE recovery model. This was a contrived failure. If you encounter any failures, please comment below.
The following are some technical points that may be of some interest.
My experience is that getting backups configured is tedious work, prone to errors. This post shows you how you can automate the entire process by just downloading the TSQL script here and executing it once on each server. The backup pattern may not be perfect, but it is better than what I have often seen. The scripts above should be robust, and best of all, if you add a database to SQL Server it will be included in the backups automatically.
I had a discussion with Shaun Tinline-Jones of the SQL Server Customer Advisory Team - SQL Server Best Practices team(SQLCAT) about where to locate this procedure and other like it. Should it be in [msdb], [model],[master], an existing ISV related database or a new database. Our conclusion was a new database named [SQLTools] was best (instead of [SQLISV] or [SQLCAT]).
To create this database
Create Database [SQLTools] Go
Some corrections:
Diff backup is all data extents that have changed since last full backup - nothing to do with prior diff backups.
Log backup is all log generated since last log backup - nothing to do with prior diff backups.
[Ken: Many thanks Paul, I have corrected my parity error. I would recommend readers to visit Paul's site, SQLSkills.com for more useful tips!]