Blog Post

Restoring databases to a set drive and directory

,

 

Restoring databases to a set drive and directory

Introduction

Often people say that necessity is the mother of invention. In this case I was faced with the dilemma of having to restore several databases, with multiple ‘ndf’ files, and having to restore them with different physical file names, drives and directories on servers other than the servers from which they originated.

As most of us would do, I went to Google to see if I could find some code to achieve this task and found some interesting snippets on Pinal Dave’s website.

Naturally, I had to take it further than the code snippet, HOWEVER it was a great place to start.

Creating a temp table to hold database file details

First off, I created a temp table which would hold the details of the individual data files within the database. Although there are a plethora of fields (within the temp table below), I utilize LogicalName only within this example. The temporary table structure may be seen below:

 

create table #tmp

(

LogicalName nvarchar(128)

,PhysicalName nvarchar(260)

,Type char(1)

,FileGroupName nvarchar(128)

,Size numeric(20,0)

,MaxSize numeric(20,0),

Fileid tinyint,

CreateLSN numeric(25,0),

DropLSN numeric(25, 0),

UniqueID uniqueidentifier,

ReadOnlyLSN numeric(25,0),

ReadWriteLSN numeric(25,0),

BackupSizeInBytes bigint,

SourceBlocSize int,

FileGroupId int,

LogGroupGUID uniqueidentifier,

DifferentialBaseLSN numeric(25,0),

DifferentialBaseGUID uniqueidentifier,

IsReadOnly bit,

IsPresent bit,

TDEThumbPrint varchar(50)

)


 

We now declare and populate a variable(@path), setting the variable to the path to our SOURCE database backup.

declare @path varchar(50)

set @path = 'P:\DATA\MYDATABASE.bak'

 

From this point, we insert the file details of our database into the temp table. Note that we do so by utilizing a restore statement HOWEVER doing so in ‘filelistonly’ mode.

 

insert #tmp

EXEC ('restore filelistonly from disk = ''' + @path + '''')

 

At this point, I depart from what I gleaned from Pinal Dave.

 

I now instantiate a few more local variables. The use of each variable will be evident within the cursor (which follows):

 

Declare @RestoreString as Varchar(max)

Declare @NRestoreString as NVarchar(max)

Declare @LogicalName  as varchar(75)

Declare @counter as int

Declare @rows as int

set @counter = 1

select @rows = COUNT(*) from #tmp  -- Count the number of records in the temp

                                   -- table

 

Declaring and populating the cursor

At this point I do realize that many people are cringing about the use of a cursor. Being an Oracle professional as well, I have learnt that there is a time and place for cursors.

I would remind the reader that the data that will be read into the cursor is from a local temp table and as such, any locking of the records (within the temp table) is not really an issue.

 

DECLARE MY_CURSOR Cursor

FOR

Select LogicalName

From #tmp

 

Parsing the logical names from within the cursor.

A small caveat that works in our favour,  is that the first logical name (of our database) is the logical name of the primary data file (.mdf). Other files, except for the very last logical name, belong to secondary data files. The last logical name is that of our database log file.

 

I now open my cursor and populate the variable @RestoreString

Open My_Cursor

set @RestoreString =

'RESTORE DATABASE [MYDATABASE] FROM DISK = N''P:\DATA\ MYDATABASE.bak''' + ' with  '

 

We now fetch the first record from the temp table.

 

Fetch NEXT FROM MY_Cursor INTO @LogicalName

 

While there are STILL records left within the cursor, we dynamically build our restore string. Note that we are using concatenation to create ‘one big restore executable string’.

 

Note also that the target physical file name is hardwired, as is the target directory.

 

While (@@FETCH_STATUS <> -1)

BEGIN

IF (@@FETCH_STATUS <> -2) -- As long as there are no rows missing

select @RestoreString =

case

when @counter = 1 then -- This is the mdf file

   @RestoreString + 'move  N''' + @LogicalName + '''' + ' TO N’’X:\DATA1\'+ @LogicalName + '.mdf' + '''' + ', '

 

-- OK, if it passes through here we are dealing with an .ndf file

-- Note that Counter must be greater than 1 and less than the number of rows.

 

when @counter > 1 and @counter < @rows then -- These are the ndf file(s)

   @RestoreString + 'move  N''' + @LogicalName + '''' + ' TO N’’X:\DATA1\'+ @LogicalName + '.ndf' + '''' + ', '

 

-- OK, if it passes through here we are dealing with the log file

When @LogicalName like '%log%' then

   @RestoreString + 'move  N''' + @LogicalName + '''' + ' TO N’’X:\DATA1\'+ @LogicalName + '.ldf' +''''

end

--Increment the counter

 

set @counter = @counter + 1

FETCH NEXT FROM MY_CURSOR INTO @LogicalName

END

 

At this point we have populated the varchar(max) variable @RestoreString with a concatenation of all the necessary file names. What we now need to do is to run the sp_executesql stored procedure, to effect the restore.

 

First, we must place our ‘concatenated string’ into an nvarchar based variable. Obviously this will only work as long as the length of @RestoreString is less than varchar(max) / 2.

 

set @NRestoreString = @RestoreString

EXEC sp_executesql @NRestoreString

 

Upon completion of this step, the database should be restored to the server.

I now close and deallocate the cursor, and to be clean, I would also drop my temp table.

 

CLOSE MY_CURSOR

DEALLOCATE MY_CURSOR

GO

 

Conclusion

Restoration of databases on different servers with different physical names and on different drives are a fact of life. Through the use of a few variables and a simple cursor, we may achieve an efficient and effective way to achieve this task.

Rate

You rated this post out of 5. Change rating

Share

Share

Rate

You rated this post out of 5. Change rating