6

We have recently upgraded from SQL Server 2005 to SQL Server 2012. Under SQL Server 2005 there is no option to create compressed backups as there is in 2012.

If you attempt BACKUP DATABASE ... WITH (COMPRESSION); to a file that has already been initialized without compression, the BACKUP DATABASE command will fail with the following error message:

ERROR MESSAGE : BACKUP DATABASE is terminating abnormally.
ERROR CODE : 3013

How can I tell if an existing backup file is initialized for compressed backups?

Hannah Vernon
  • 70,928
  • 22
  • 177
  • 323

5 Answers5

7

Maybe instead of backing up over and over again to the same file, you should consider using WITH INIT and a new file always. I think it is simpler to manage multiple backup files, all with their own timestamp embedded into the filename, and be able to archive/purge each file individually. When you keep dumping backups to the same file, it just gets bigger and bigger, and harder to manage IMHO. Never mind that you no longer have to care that

Also I am not sure why you would ever be turning backup compression on and off. Have you found a case where disabling it is better? Do you have real use cases where on an edition that supports compression you are taking one-off backups without compression, and using the same file? Why?

Anyway you can always do something very simple like:

BEGIN TRY
    BACKUP DATABASE x TO DISK = 'c:\wherever\x.bak' WITH COMPRESSION, ...;
END TRY
BEGIN CATCH
    BACKUP DATABASE x TO DISK = 'c:\wherever\x.bak', ...;
END CATCH

The error bubbles up immediately before any work is done.

But I still think it is much better to just not use the same file over and over again in the first place. IMHO.

Aaron Bertrand
  • 181,950
  • 28
  • 405
  • 624
6

I created the following stored procedure that can be used to determine if a database backup file is initialized for compression:

CREATE PROCEDURE IsBackupFileCompressed
(
    @BackupFileName nvarchar(255)
    , @UseXPFileExists bit = 1
)
AS
BEGIN
    /*
        Inspects the header of the given backup file to see if the
        file contains a SQL Server compressed backup.

        Returns 1 if the backup is compressed, 0 if uncompressed.

        By:     Max Vernon
        Date:   2013-03-26
    */
    SET NOCOUNT ON;
    DECLARE @FileExists bit;
    DECLARE @Compressed bit;
    DECLARE @cmd nvarchar(max);
    DECLARE @ShellText NVARCHAR(512);
    DECLARE @ShellResults TABLE (
        ShellText nvarchar(255)
    );
    DECLARE @Exists TABLE 
    (
        [File Exists] bit
        , [File is a Directory] bit
        , [Parent Directory Exists] bit
    );
    DECLARE @t TABLE (
        BackupName nvarchar(255)
        , BackupDescription nvarchar(255)
        , BackupType    int
        , ExpirationDate    datetime
        , Compressed    int
        , Position  int
        , DeviceType    int
        , UserName  nvarchar(255)
        , ServerName    nvarchar(255)
        , DatabaseName  nvarchar(255)
        , DatabaseVersion   int
        , DatabaseCreationDate  datetime
        , BackupSize    numeric(38,0)
        , FirstLSN  numeric(38,0)
        , LastLSN   numeric(38,0)
        , CheckpointLSN numeric(38,0)
        , DatabaseBackupLSN numeric(38,0)
        , BackupStartDate   datetime
        , BackupFinishDate  datetime
        , SortOrder int
        , CodePage  int
        , UnicodeLocaleId   int
        , UnicodeComparisonStyle    int
        , CompatibilityLevel    int
        , SoftwareVendorId  int
        , SoftwareVersionMajor  int
        , SoftwareVersionMinor  int
        , SoftwareVersionBuild  int
        , MachineName   nvarchar(255)
        , Flags int
        , BindingID uniqueidentifier    
        , RecoveryForkID    uniqueidentifier
        , Collation nvarchar(255)
        , FamilyGUID    uniqueidentifier
        , HasBulkLoggedData int
        , IsSnapshot    int
        , IsReadOnly    int
        , IsSingleUser  int
        , HasBackupChecksums    int
        , IsDamaged int
        , BeginsLogChain    int
        , HasIncompleteMetaData int
        , IsForceOffline    int
        , IsCopyOnly    int
        , FirstRecoveryForkID   uniqueidentifier
        , ForkPointLSN  numeric(38,0)
        , RecoveryModel nvarchar(255)
        , DifferentialBaseLSN   numeric(38,0)
        , DifferentialBaseGUID  uniqueidentifier
        , BackupTypeDescription nvarchar(255)
        , BackupSetGUID uniqueidentifier
        , CompressedBackupSize  numeric(38,0)
        , Containment int
    );

    SET @FileExists = 0;
    IF @UseXPFileExists = 1
    BEGIN
        DECLARE @IsXPCmdShellEnabled BIT;
        SET @IsXPCmdShellEnabled = CAST(
            (
                SELECT top(1) value_in_use 
                FROM sys.configurations c 
                WHERE c.name = 'xp_cmdshell'
            ) as bit);
        IF @IsXPCmdShellEnabled = 1 
        BEGIN
            SET @ShellText = 'dir /b ' + @BackupFileName
            INSERT INTO @ShellResults
            exec xp_cmdshell @ShellText;
            SELECT @FileExists = COUNT(*) 
                FROM @ShellResults S 
                WHERE @BackupFileName LIKE ('%' + s.ShellText) 
                    AND s.ShellText IS NOT NULL;
        END
        ELSE
        BEGIN
            /*
                This is a fallback in case XP_CMDSHELL is disabled
                Unfortunately, this will trigger a SEV16 error if the file does not exist, 
                setting off alarm bells all over the place
            */
            BEGIN TRY
                SET @cmd = 'RESTORE LABELONLY FROM DISK=''' + @BackupFileName + ''';';
                EXEC sp_executesql @cmd;
                SET @FileExists = 1;
            END TRY
            BEGIN CATCH
                SET @FileExists = 0;
            END CATCH
        END
    END
    ELSE
    BEGIN
        INSERT INTO @Exists
        EXEC Master.dbo.xp_fileexist @BackupFileName;
        SELECT @FileExists = [File Exists] FROM @Exists E;
    END
    SET @Compressed = 0;
    IF @FileExists > 0
    BEGIN
        SET @cmd = 'RESTORE HEADERONLY FROM DISK=''' + @BackupFileName + ''';';
        INSERT INTO @t
        EXEC sp_executesql @cmd;
        SELECT @Compressed = Compressed FROM @t t;
    END
    SELECT @Compressed;
END
GO

This stored proc demonstrates how to determine if a file exists in various ways, including using xp_cmdshell with a TRY...CATCH block in case xp_cmdshell is not enabled, and my preferred method, xp_fileexists.

I'm not incredibly happy with how this stored proc works - I would prefer a more light-weight version.

Hannah Vernon
  • 70,928
  • 22
  • 177
  • 323
6

Here's a handy powershell function to do the same thing:

Function IsBackupCompressed
{
    Param
        (
            [string]$Server,
            [string]$BAKFile
        )

    [Void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SQLServer.SMO')
    [Void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SQLServer.SMOExtended')

    $SMOServer = New-Object Microsoft.SqlServer.Management.Smo.Server $Server
    $Res = New-Object Microsoft.SqlServer.Management.Smo.Restore

    $Res.Devices.AddDevice($BAKFile, [Microsoft.SqlServer.Management.Smo.DeviceType]::File)

    $Res.ReadBackupHeader($SMOServer).Rows[0].Compressed


}

Pass it your server name and path to the .BAK file and it will return a 1 or 0 for compression.

Bear in mind the .BAK file path needs to be relative to wherever you run the script from!

If this is going into production you should add some error trapping for things like the file not existing.

JNK
  • 18,064
  • 6
  • 63
  • 98
6

To see if a particular database backup file was backed up using compression or not you can interrorgate the header information of the file:

RESTORE HEADERONLY FROM DISK = 'Path to backup file';

One of the columns returned is labelled "Compressed". 1 = compressed, 0 = not compressed.

You could load that info into a temp table if you need to have some control flow about whether you can reuse the file or need to reinitialise it. Something like:

DECLARE @RestoreHeader TABLE (
    BackupName NVARCHAR(128),
    BackupDescription NVARCHAR(255),
    BackupType SMALLINT,
    ExpirationDate DATETIME,
    Compressed BIT,
    Position SMALLINT,
    DeviceType TINYINT,
    UserName NVARCHAR(128),
    ServerName NVARCHAR(128),
    DatabaseName NVARCHAR(128),
    DatabaseVersion INT,
    DatabaseCreationDate DATETIME,
    BackupSize NUMERIC(20,0),
    FirstLSN NUMERIC(25,0),
    LastLSN NUMERIC(25,0),
    CheckpointLSN NUMERIC(25,0),
    DatabaseBackupLSN NUMERIC(25,0),
    BackupStartDate DATETIME,
    BackupFinishDate DATETIME,
    SortOrder SMALLINT,
    CodePage SMALLINT,
    UnicodeLocaleId INT,
    UnicodeComparisonStyle INT,
    CompatibilityLevel TINYINT,
    SoftwareVendorId INT,
    SoftwareVersionMajor INT,
    SoftwareVersionMinor INT,
    SoftwareVersionBuild INT,
    MachineName NVARCHAR(128),
    Flags  INT,
    BindingID UNIQUEIDENTIFIER,
    RecoveryForkID UNIQUEIDENTIFIER,
    Collation NVARCHAR(128),
    FamilyGUID UNIQUEIDENTIFIER,
    HasBulkLoggedData BIT,
    IsSnapshot BIT,
    IsReadOnly BIT,
    IsSingleUser BIT,
    HasBackupChecksums BIT,
    IsDamaged BIT,
    BeginsLogChain BIT,
    HasIncompleteMetaData BIT,
    IsForceOffline BIT,
    IsCopyOnly BIT,
    FirstRecoveryForkID UNIQUEIDENTIFIER,
    ForkPointLSN NUMERIC(25,0) NULL,
    RecoveryModel NVARCHAR(60),
    DifferentialBaseLSN NUMERIC(25,0) NULL,
    DifferentialBaseGUID UNIQUEIDENTIFIER,
    BackupTypeDescription NVARCHAR(60),
    BackupSetGUID UNIQUEIDENTIFIER NULL,
    CompressedBackupSize BIGINT,
    containment TINYINT NOT NULL
);

INSERT INTO @RestoreHeader
EXEC ('RESTORE HEADERONLY FROM DISK = ''Path to backup file''');

SELECT Compressed FROM @RestoreHeader
Kent Chenery
  • 431
  • 2
  • 4
2

I'm not sure the following will help you (as there have been many great answers), but if you have access to the backup history, I guess you could determine whether previous backups were compressed or not with a query similar to this one:

select top(10) database_name, 
       case 
            when  backup_size = compressed_backup_size then 'Compressed'
            else 'Not compressed'
        end as Compression, 
        backup_finish_date
from msdb.dbo.backupset
where database_name ='myDB'
order by backup_start_date desc
KookieMonster
  • 2,037
  • 19
  • 18