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.