Check for full SQL Server database backups before creating other backups

Problem

For very large databases, we usually include either differential, file or filegroup backups together with the full database backup in our backup plan. In some cases, differential backups are used before a major change is implemented in the database. The backups can, then, be used to rollback those changes if and when necessary. The challenge with using these other types of backups is that they require a full backup as the starting point for the restore process. How do we make sure that we have a copy of the full database backups before we generate these other types of backups?

Solution

In a previous tip on Differential Database Backups for SQL Server, you have seen how differential backups work in combination with full database backups. However, you need to make sure that, if you are generating differential backups or any other type of backups other than the full database backup, the full database backup should be available. This is specifically helpful in cases where you would need to implement a major change in your database and would like to have a backup available for rollback purposes in case the need arise. I have seen organizations where they do not have the benefits of having database snapshots in SQL Server for rollback purposes. As this is an Enterprise Edition-only feature, they would opt to use both a full and differential backups for the purpose of rolling back changes.

The script below will create a stored procedure that is used to generate differential backups. You can also change it to any other type of backups but, for the purpose of this tip, I will be using a differential backup.

The stored procedure works by checking the backup history and searches for the latest full database backup and the corresponding file. If the latest full database backup does not exist or the database has never been backed up, the differential backup will abort. The stored procedure accepts a parameter that will contain the location of the differential backup with a default value of your pre-defined backup location.

CREATE PROCEDURE sp_generateDifferentialBackups  
   @strBackupPath NVARCHAR (25) ='D:\backups\' --variable for location of DIFFERENTIAL backups  
AS  
SET NOCOUNT ON   
-- Get the name of all user databases    
DECLARE @strTimeStamp NVARCHAR(12)   --variable for timestamp value   
DECLARE @strSQL NVARCHAR(100)        -- used for generating dynamic SQL statements   
DECLARE @databaseName NVARCHAR(128)  -- used as variable to store database names   
DECLARE dbCursor CURSOR FOR          -- used for cursor allocation   
   SELECT NAME  
   FROM MASTER.SYS.DATABASES  
   WHERE [database_id] > 0  
   AND NAME NOT IN ('MASTER','MODEL','MSDB','TEMPDB') --does not include the system databases   
OPEN dbCursor   
   
FETCH NEXT FROM dbCursor INTO @databaseName   
   
WHILE (@@FETCH_STATUS = 0)   
BEGIN   
   PRINT 'Checking for the latest FULL database backup for: [' + @databaseName +']'  
       
   DECLARE @strphysical_device_name NVARCHAR(100) -- variable for physical_device_name   
   DECLARE @cursor NVARCHAR(400)   
   -- Reads the MSDB database to check for the latest FULL database backup         
   SELECT @cursor=('DECLARE TabCursor CURSOR FAST_FORWARD GLOBAL FOR ' +   
   'SELECT TOP 1 physical_device_name   
   FROM msdb.dbo.backupset a INNER JOIN msdb.dbo.backupmediafamily b      
   ON a.media_set_id = b.media_set_id      
   WHERE database_name=''' + @databaseName + '''   
   AND type=''D''   
   ORDER BY backup_finish_date desc')   
   EXEC sp_executesql @cursor   
   OPEN TabCursor   
   
   FETCH NEXT FROM TabCursor INTO @strphysical_device_name   
   --Check if the database does not have any FULL backups at all  
   IF @@FETCH_STATUS <> 0  
   BEGIN  
      PRINT '*****WARNING: Database [' + @databaseName + '] does not have any FULL database backups at all. Generate the FULL database backup first. Aborting DIFFERENTIAL backup command.*****'  
   END  
   ELSE     
   --If the database has FULL backups,  
   WHILE @@FETCH_STATUS = 0   
   BEGIN   
      DECLARE @result INT   
      EXEC xp_fileexist @strphysical_device_name, @result output    
      IF (@result = 1)  --@result will return 1 if the specified file exists, if it doesn't, it will return 0   
      BEGIN   
         SET @strTimeStamp=CONVERT(CHAR(8), GETDATE(), 112)   
         SET @strTimeStamp=@strTimeStamp + REPLACE(CONVERT(CHAR(8), GETDATE(), 108),':','')   
         EXEC ('BACKUP DATABASE ' + @databaseName + ' TO DISK=''' + @strBackupPath + @databaseName + '_DIFF_' + @strTimeStamp + '.BAK'' WITH INIT, DIFFERENTIAL, DESCRIPTION=''DIFFERENTIAL Backup for ' + @databasename + ' database''') -- change this value should you decide to change the backup type to something other than differential    
         PRINT '==========================================================================================='   
         PRINT 'DIFFERENTIAL Backup generated for database: [' + @databaseName  + ']'  
         PRINT 'Corresponding FULL database backup: ' + @strphysical_device_name   
         PRINT 'DIFFERENTIAL database backup: ' + @strBackupPath + @databaseName + '_DIFF_' + @strTimeStamp + '.BAK'   
      END   
      ELSE   
         PRINT  '*********WARNING: FULL database backup file is missing. Generate the FULL database backup first. Aborting DIFFERENTIAL backup command.*********'   
          
      FETCH NEXT FROM TabCursor INTO @strphysical_device_name   
   END   
   CLOSE TabCursor   
   DEALLOCATE TabCursor   
        
   PRINT '==========================================================================================='   
   PRINT '                                                                                           '   
   PRINT '                                                                                           '   
   PRINT '                                                                                           '   
    
   FETCH NEXT FROM dbCursor INTO @databaseName   
END   
   
CLOSE dbCursor   
DEALLOCATE dbCursor   
PRINT 'FINISHED'   
GO   

You can either pass a parameter that contains the backup location where you intend to store the differential backup or simply call the stored procedure as it is.

sp_generateDifferentialBackups 'D:\backups\'

Here sample output after creating and running the stored procedure.

full backup output report

Aside from using this stored procedure to generate ad-hoc differential backups, this can also be used as an alternative to your database maintenance plans. Instead of using the Backup Database Task, you can use an Execute TSQL Task and call this stored procedure. By doing so, you are practically sure that your differential backups will have their corresponding full database backups. You can customize this script to send alerts when the differential backup is aborted to let you know that you either have a missing full database backup or that your database has not been backed up yet.

Next Steps

One comment

  1. Nice script! Checking for a full backup before running differential backups is a smart move. This ensures you’re covered if you need to roll back changes.

Leave a Reply

Your email address will not be published. Required fields are marked *