• SQL Server
  • Log Shipping Tricks Demo
  • SQLCruise Alaska 2012 Pics
SQLSoldier News From the Frontlines

Day 3 of 31 Days of Disaster Recovery: Determining Files to Restore Database

January 3, 2013 11:43 pm / 28 Comments / SQLSoldier

Day 3 of 31 Days of Disaster Recovery: Determining Files to Restore Database

31 Days of Disaster Recovery

31 Days of Disaster Recovery

Welcome back for day 3 of my month-long series on Disaster Recovery. For today’s post, I want to address something that, in my experience, has really flummoxed a lot of people who find themselves unprepared to handle a disaster scenario. Determining exactly which files you should restore.

If you missed any of the earlier posts in the series, you can check them out here:

    31 Days of disaster Recovery

  1. Does DBCC Automatically Use Existing Snapshot?
  2. Protection From Restoring a Backup of a Contained Database

Test Your Restores

Everyone on your team who might be called upon to perform a restore should be performing regular test runs of restoring the backups. It’s this lack of testing that leads to fumbling around trying to figure out what files to restore. Well, whether you’ve practiced or not, you might like a little help to figure out exactly what files are required to restore the database to its most current point. Thus I give you my RestoreScripter script. The script traverses the backup information in the backup tracking tables in the msdb database.

The script automatically handles many of the possible pitfalls that you might experience and gives you the details you need to quickly script out the restores. For example, records with the same “RestoreOrder” are multiple files for the same backup and should be scripted together in a single restore command.

The Script

As usual, rather than explain the various steps in the script here, I have included extensive comments inline in the script at each step. You can grab the script below or download it as a zipped file here: RestoreScripter.zip (2 KB)

Declare @DBName sysname,
    @DBBackupLSN numeric(25, 0);
Declare @Baks Table (
    BakID int identity(1, 1) not null primary key,
    backup_set_id int not null,
    media_set_id int not null,
    first_family_number tinyint not null,
    last_family_number tinyint not null,
    first_lsn numeric(25, 0) null,
    last_lsn numeric(25, 0) null,
    database_backup_lsn numeric(25, 0) null,
    backup_finish_date datetime null,
    type char(1) null,
    family_sequence_number tinyint not null,
    physical_device_name nvarchar(260) not null,
    device_type tinyint null)

Set NoCount On;

-- Set the name of the database you want to restore
Set @DBName = N'';

-- Get the most recent full backup with all backup files
Insert Into @Baks (backup_set_id,
    media_set_id,
    first_family_number,
    last_family_number,
    first_lsn,
    last_lsn,
    database_backup_lsn,
    backup_finish_date,
    type,
    family_sequence_number,
    physical_device_name,
    device_type)
Select Top(1) With Ties B.backup_set_id,
    B.media_set_id,
    B.first_family_number,
    B.last_family_number,
    B.first_lsn,
    B.last_lsn,
    B.database_backup_lsn,
    B.backup_finish_date,
    B.type,
    BF.family_sequence_number,
    BF.physical_device_name,
    BF.device_type
From msdb.dbo.backupset As B
Inner Join msdb.dbo.backupmediafamily As BF
    On BF.media_set_id = B.media_set_id
        And BF.family_sequence_number Between B.first_family_number And B.last_family_number
Where B.database_name = @DBName
And B.is_copy_only = 0
And B.type = 'D'
And BF.physical_device_name Not In ('Nul', 'Nul:')
Order By backup_finish_date desc, backup_set_id;

-- Get the lsn that the differential backups, if any, will be based on
Select @DBBackupLSN = database_backup_lsn
From @Baks;

-- Get the most recent differential backup based on that full backup
Insert Into @Baks (backup_set_id,
    media_set_id,
    first_family_number,
    last_family_number,
    first_lsn,
    last_lsn,
    database_backup_lsn,
    backup_finish_date,
    type,
    family_sequence_number,
    physical_device_name,
    device_type)
Select Top(1) With Ties B.backup_set_id,
    B.media_set_id,
    B.first_family_number,
    B.last_family_number,
    B.first_lsn,
    B.last_lsn,
    B.database_backup_lsn,
    B.backup_finish_date,
    B.type,
    BF.family_sequence_number,
    BF.physical_device_name,
    BF.device_type
From msdb.dbo.backupset As B
Inner Join msdb.dbo.backupmediafamily As BF
    On BF.media_set_id = B.media_set_id
        And BF.family_sequence_number Between B.first_family_number And B.last_family_number
Where B.database_name = @DBName
And B.is_copy_only = 0
And B.type = 'I'
And BF.physical_device_name Not In ('Nul', 'Nul:')
And B.database_backup_lsn = @DBBackupLSN
Order By backup_finish_date Desc, backup_set_id;

-- Get the last LSN included in the differential backup,
-- if one was found, or of the full backup
Select Top 1 @DBBackupLSN = last_lsn
From @Baks
Where type In ('D', 'I')
Order By BakID Desc;

-- Get first log backup, if any, for restore, where
-- last_lsn of previous backup is >= first_lsn of the
-- log backup and <= the last_lsn of the log backup
Insert Into @Baks (backup_set_id,
    media_set_id,
    first_family_number,
    last_family_number,
    first_lsn,
    last_lsn,
    database_backup_lsn,
    backup_finish_date,
    type,
    family_sequence_number,
    physical_device_name,
    device_type)
Select Top(1) With Ties B.backup_set_id,
    B.media_set_id,
    B.first_family_number,
    B.last_family_number,
    B.first_lsn,
    B.last_lsn,
    B.database_backup_lsn,
    B.backup_finish_date,
    B.type,
    BF.family_sequence_number,
    BF.physical_device_name,
    BF.device_type
From msdb.dbo.backupset B
Inner Join msdb.dbo.backupmediafamily As BF
    On BF.media_set_id = B.media_set_id
        And BF.family_sequence_number Between B.first_family_number And B.last_family_number
Where B.database_name = @DBName
And B.is_copy_only = 0
And B.type = 'L'
And BF.physical_device_name Not In ('Nul', 'Nul:')
And @DBBackupLSN Between B.first_lsn And B.last_lsn
Order By backup_finish_date, backup_set_id;

-- Get last_lsn of the first log backup that will be restored
Set @DBBackupLSN = Null;
Select @DBBackupLSN = Max(last_lsn)
From @Baks
Where type = 'L';

-- Recursively get all log backups, in order, to be restored
-- first_lsn of the log backup = last_lsn of the previous log backup
With Logs
As (Select B.backup_set_id,
        B.media_set_id,
        B.first_family_number,
        B.last_family_number,
        B.first_lsn,
        B.last_lsn,
        B.database_backup_lsn,
        B.backup_finish_date,
        B.type,
        BF.family_sequence_number,
        BF.physical_device_name,
        BF.device_type,
        1 As LogLevel
    From msdb.dbo.backupset B
    Inner Join msdb.dbo.backupmediafamily As BF
        On BF.media_set_id = B.media_set_id
            And BF.family_sequence_number Between B.first_family_number And B.last_family_number
    Where B.database_name = @DBName
    And B.is_copy_only = 0
    And B.type = 'L'
    And BF.physical_device_name Not In ('Nul', 'Nul:')
    And B.first_lsn = @DBBackupLSN
    Union All
    Select B.backup_set_id,
        B.media_set_id,
        B.first_family_number,
        B.last_family_number,
        B.first_lsn,
        B.last_lsn,
        B.database_backup_lsn,
        B.backup_finish_date,
        B.type,
        BF.family_sequence_number,
        BF.physical_device_name,
        BF.device_type,
        L.LogLevel + 1
    From msdb.dbo.backupset B
    Inner Join msdb.dbo.backupmediafamily As BF
        On BF.media_set_id = B.media_set_id
            And BF.family_sequence_number Between B.first_family_number And B.last_family_number
    Inner Join Logs L On L.database_backup_lsn = B.database_backup_lsn
    Where B.database_name = @DBName
    And B.is_copy_only = 0
    And B.type = 'L'
    And BF.physical_device_name Not In ('Nul', 'Nul:')
    And B.first_lsn = L.last_lsn)
Insert Into @Baks (backup_set_id,
    media_set_id,
    first_family_number,
    last_family_number,
    first_lsn,
    last_lsn,
    database_backup_lsn,
    backup_finish_date,
    type,
    family_sequence_number,
    physical_device_name,
    device_type)
Select backup_set_id,
    media_set_id,
    first_family_number,
    last_family_number,
    first_lsn,
    last_lsn,
    database_backup_lsn,
    backup_finish_date,
    type,
    family_sequence_number,
    physical_device_name,
    device_type
From Logs
Option(MaxRecursion 0);

-- Select out just the columns needed to script restore
Select RestoreOrder = Row_Number() Over(Partition By family_sequence_number Order By BakID),
    RestoreType = Case When type In ('D', 'I') Then 'Database'
            When type = 'L' Then 'Log'
        End,
    DeviceType = Case When device_type in (2, 102) Then 'Disk'
            When device_type in (5, 105) Then 'Tape'
        End,
    PhysicalFileName = physical_device_name
From @Baks
Order By BakID
;

Set NoCount Off;

Sample Output

Sample Restore Files

Sample Restore Files

Posted in: SQL Server / Tagged: 31 Days of Disaster Recovery, Disaster Recovery, T-SQL

28 Thoughts on “Day 3 of 31 Days of Disaster Recovery: Determining Files to Restore Database”

  1. Paul Brewer on January 4, 2013 at 5:56 am said:

    Hi,
    I’d love to hear your opinion on a procedure I’ve just developed that does the same as your script. It includes a STOPAT option which can be used to construct restore scripts based on historic backups/diffs/logs, IE, not necessarily the most recent full.

    http://paul.dynalias.com/SQL

    Best wishes
    Paul

    Reply↓
    • SQLSoldier on January 4, 2013 at 7:31 am said:

      Great script, Paul. One of the things I was trying to demonstrate in this script was basing the association between the different types of backups on the appropriate LSN value. I think that’s safer than basing it only on date.

      Reply↓
  2. Paul Brewer on January 4, 2013 at 8:02 am said:

    Your script has shown me the way really, I’ll add a check of the LSN.
    Thanks and regards

    Reply↓
  3. Timothy Batts on January 4, 2013 at 8:06 am said:

    I gave the script a test run, but every database I ran it against that is in the full recovery model I received the following error: Msg 530, Level 16, State 1, Line 151
    The statement terminated. The maximum recursion 100 has been exhausted before statement completion.

    Thoughts?

    Reply↓
    • SQLSoldier on January 4, 2013 at 8:14 am said:

      That’s a lot of log backups per database, but after thinking about it, if you back up the log every 5 minutes, you could reach that in a little more than 8 hours. I hadn’t thought of that. Thanks for bringing this up. I’ll add the MaxRecursion hint to the recursive CTE.

      Reply↓
      • Wayne on January 11, 2013 at 9:03 am said:

        I think you added the hint to the wrong query.

        Reply↓
        • SQLSoldier on January 11, 2013 at 9:44 am said:

          Thanks Wayne! You are right. I did get it right in the actual script that’s attached, but I seem to have trouble finding the right place inside the HTML of the post.

          Reply↓
    • SQLSoldier on January 4, 2013 at 8:21 am said:

      Change made.

      Reply↓
  4. Anup Warrier on January 4, 2013 at 3:39 pm said:

    Thats an excellent script Robert,really helpful.

    Reply↓
    • SQLSoldier on January 4, 2013 at 5:24 pm said:

      Thanks Anup!

      Reply↓
  5. Paul Brewer on January 4, 2013 at 4:54 pm said:

    My procedure has been changed to use the backup LSN’s as you do, your join condition to backup media set was also added. You are credited at the bottom , the article and script have been submitted to sql server central. Your series of posts on DR planning are great and very useful.
    Thanks

    Reply↓
    • SQLSoldier on January 4, 2013 at 5:25 pm said:

      Cool! Thanks for giving me credit! I look forward to seeing it posted.

      Reply↓
  6. Pingback: Day 6 of 31 Days of Disaster Recovery: Dealing With Corruption in Allocation Pages | SQLSoldier

  7. Pingback: Day 7 of 31 Days of Disaster Recovery: Writing SLAs for Disaster Recover | SQLSoldier

  8. Pingback: Day 8 of 31 Days of Disaster Recovery (T-SQL Tuesday #38): Resolutions for All DBAs | SQLSoldier

  9. Pingback: Day 9 of 31 Days of Disaster Recovery: Use All the Checksums | SQLSoldier

  10. Pingback: Day 5 of 31 Days of Disaster Recovery: Dealing With Corruption in a Nonclustered Index | SQLSoldier

  11. Wayne on January 11, 2013 at 9:48 am said:

    If you run this against a fresh 2012 install against a system database, is shows a backup for a virtual device, but this is null in the output (the case statement doesn’t consider device_type = 7). And, I don’t think that these can be restored in any case (aren’t they just like backing up to the NUL device?), so would you want to exclude this device type from the queries?

    Reply↓
    • SQLSoldier on January 11, 2013 at 9:59 am said:

      Thanks Wayne. Yes, I think those should be excluded.

      Reply↓
  12. Pingback: Day 11 of 31 Days of Disaster: Converting LSN Formats | SQLSoldier

  13. Pingback: Day 12 of 31 Days of Disaster Recovery: Extreme Disaster Recovery Training | SQLSoldier

  14. Pingback: Day 14 of 31 Days of Disaster Recovery: Fixing a Corrupt Tempdb | SQLSoldier

  15. Pingback: Day 15 of 31 Days of Disaster Recovery: Running DBCC CheckTable in Parallel Jobs | SQLSoldier

  16. Rick on January 25, 2013 at 12:04 pm said:

    Fantastic script! Thank you very much for sharing your knowledge. It is greatly appreciated.

    Reply↓
    • SQLSoldier on January 25, 2013 at 12:44 pm said:

      Thanks Rick!

      Reply↓
  17. Pingback: Day 24 of 31 Days of Disaster Recovery: Handling Corruption in a Clustered Index | SQLSoldier

  18. Wes Brown on March 12, 2013 at 12:25 pm said:

    Quick note: The virtual backup devices are usually third party tools backing up your databases. When things like backup exec backup SQL Server they call VDI that generates a virtual device and assigns it a GUID for the device name.

    -Wes

    Reply↓
    • SQLSoldier on March 12, 2013 at 1:06 pm said:

      Thanks Wes.

      Reply↓

Leave a Reply Cancel reply

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

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Post Navigation

← Previous Post
Next Post →
<

Remote DBA Services
- serious SQL Server expertise for less than a full-time DBA
My Articles
 
My Book
Check out my interview on

Extreme Data Recovery (with Argenis Fernandez)
10 Things all BI System Administrators Should Know
Upcoming Events
    All events shown in Pacific Time

    No events to show

RSS My SQL Server Magazine Articles

  • Database Mirroring for Disaster Recovery September 16, 2011
  • Comparative Review: Database Schema Comparison Tools August 24, 2011
  • 3 Log Shipping Techniques June 22, 2011
  • Hardening SQL Server June 20, 2011
  • Review: ScriptLogic Security Explorer for SQL Server February 8, 2011

Tags

31 Days of Disaster Recovery Architecture Automation CDC & Change Tracking Data Architecture VC Database Mirroring DBCC Denali Disaster Recovery Dynamic Management Views Extended Events Gamers & Geeks General Discussion High Availability How do I ... ? Humor Idera ACE Program Internals MCM Meme Monday Performance & Optimization PowerShell Professional Development Replication Security SQLBits SQL PASS SQL PASS Summit SQLRally SQL Saturday SQL Server Magazine SQL University SSAS & BI SSIS SSMS SSRS T-SQL T-SQL Tuesday tempDB Tips & Tricks Travel Troubleshooting Undocumented Stuff Whitepapers XML in SQL

News

Download my Powershell Scripts

The following scripts can be downloaded as text files. You will need to change the file extension to .ps1 in order to execute them.

Backup a database
Restore a database
Scan a server to find a free port
Query DNS to get the FQDN of a server


To see some examples of my other forms of writing, please visit my page on WritersCafe.org. It is almost exclusively horror fiction, but I sometimes throw other things in there too from time to time. There's one science fiction story, a couple of poems, and quite a few humor pieces as well.


Look for me in the SQL Q&A section of the August, 2007 issue of TechNet Magazine.
August issue of TechNet Magazine's SQL Q&A column

Protect our Heroes

© Copyright 2012 - Robert L Davis
Infinity Theme by DesignCoral / WordPress

Twitter Twitter 
LinkedIn LinkedIn 
TLF TLF RSS RSS 
WritersCafe WritersCafe 
SQLPASS SQLPASS 
Facebook Facebook
grab this