Restore Database backup consisting of multiple files via script - sql-server

I frequently need to restore database from backup files on my SQL server and I have a configurable script that takes care of that. Basically the script is using RESTORE command and has variables all over to replace database name, bak file folder, etc. However, one issue I face is restoring a database which is split into multiple files, at times the count goes up till 100.
So my question is how do I make the script dynamic so that I can just specify a file count and the script will iterate through all files and restore it. From what I understand the RESTORE command does not allow this flexibility.
One option that I see right now is to dynamically generate the entire RESTORE command and then execute it, but I want to use it only as a last option.
Are there any other options?
Thanks,

I am not sure that I understand you about "specify a file count", but I use some kind of script for implement testing environment for myself. I hope it helps you. Feel free to ask me
declare #fileListTable table
(
LogicalName nvarchar(128),
PhysicalName nvarchar(260),
[Type] char(1),
FileGroupName nvarchar(128),
Size numeric(20,0),
MaxSize numeric(20,0),
FileID bigint,
CreateLSN numeric(25,0),
DropLSN numeric(25,0),
UniqueID uniqueidentifier,
ReadOnlyLSN numeric(25,0),
ReadWriteLSN numeric(25,0),
BackupSizeInBytes bigint,
SourceBlockSize int,
FileGroupID int,
LogGroupGUID uniqueidentifier,
DifferentialBaseLSN numeric(25,0),
DifferentialBaseGUID uniqueidentifier,
IsReadOnl bit,
IsPresent bit,
TDEThumbprint varbinary(32)
)
insert into #fileListTable exec('restore filelistonly from disk = ''C:\Share\BackUp\Reporting\Prod23.bak''') -- hear I get all files from backup
select * from #fileListTable
declare #sctript nvarchar(max)
select
#sctript = 'restore database Prod from disk = ''C:\Share\BackUp\Reporting\Prod23.bak'' WITH FILE = 1,' +
STUFF( (SELECT results.MoveTo + ' , '
from
(
select
'MOVE ''' + LogicalName+ ''' TO ''E:\MobiledbnkDB_Report\' + LogicalName +
case [Type]
when 'D' then '.mdf'
when 'L' then '.ldf'
end + ''' ' as MoveTo
FROM #fileListTable -- hear I mode files to another folder
) as results
FOR XML PATH('')),
1, 0, '')
+ ' NOUNLOAD, STATS = 5'
use master
if exists(SELECT * FROM sys.databases d WITH(NOLOCK) where d.name = 'Prod')
begin
alter database Prod set restricted_user with rollback immediate
drop database Prod
end
print #sctript
exec (#sctript)

I think what you are looking for is a solution I came up with to restore uncompressed LiteSpeed files. Since the amount of files differs based on how many threads litespeed had available at the time of the backup, we never know how many files we will need to include to restore to our reporting environment. I used Vladimir's use of the stuff function and another script to do the following. Replace FILELOCATION with your folder and update your database, data and log file names.
Declare #BackupFolder nvarchar(100) = N'FILELOCATION'
DECLARE #BackupDirectory SYSNAME = #BackupFolder
IF OBJECT_ID('tempdb..#DirTree') IS NOT NULL
DROP TABLE #DirTree
CREATE TABLE #DirTree (
Id int identity(1,1),
SubDirectory nvarchar(255),
Depth smallint,
FileFlag bit,
ParentDirectoryID int
)
INSERT INTO #DirTree (SubDirectory, Depth, FileFlag)
EXEC master..xp_dirtree #BackupDirectory, 10, 1
UPDATE #DirTree
SET ParentDirectoryID = (
SELECT MAX(Id) FROM #DirTree d2
WHERE Depth = d.Depth - 1 AND d2.Id < d.Id
)
FROM #DirTree d
DECLARE
#ID INT,
#BackupFile VARCHAR(MAX),
#Depth TINYINT,
#FileFlag BIT,
#ParentDirectoryID INT,
#wkSubParentDirectoryID INT,
#wkSubDirectory VARCHAR(MAX)
DECLARE #BackupFiles TABLE
(
FileNamePath VARCHAR(MAX),
TransLogFlag BIT,
BackupFile VARCHAR(MAX),
DatabaseName VARCHAR(MAX)
)
DECLARE FileCursor CURSOR LOCAL FORWARD_ONLY FOR
SELECT * FROM #DirTree WHERE FileFlag = 1
OPEN FileCursor
FETCH NEXT FROM FileCursor INTO
#ID,
#BackupFile,
#Depth,
#FileFlag,
#ParentDirectoryID
SET #wkSubParentDirectoryID = #ParentDirectoryID
WHILE ##FETCH_STATUS = 0
BEGIN
--loop to generate path in reverse, starting with backup file then prefixing subfolders in a loop
WHILE #wkSubParentDirectoryID IS NOT NULL
BEGIN
SELECT #wkSubDirectory = SubDirectory, #wkSubParentDirectoryID = ParentDirectoryID
FROM #DirTree
WHERE ID = #wkSubParentDirectoryID
SELECT #BackupFile = #wkSubDirectory + '\' + #BackupFile
END
SELECT #BackupFile = #BackupDirectory + #BackupFile
INSERT INTO #BackupFiles (FileNamePath) VALUES(#BackupFile)
FETCH NEXT FROM FileCursor INTO
#ID,
#BackupFile,
#Depth,
#FileFlag,
#ParentDirectoryID
SET #wkSubParentDirectoryID = #ParentDirectoryID
END
CLOSE FileCursor
DEALLOCATE FileCursor
SELECT #BackupFolder + '\' + SubDirectory from #DirTree
Declare #filecount int = (select count(*) from #DirTree)
declare #sctript nvarchar(max)
select
#sctript = 'RESTORE DATABASE rrrealty FROM ' +
STUFF( (SELECT +',' + results.MoveTo
from
(
select 'DISK = N''' + 'FILELOCATION' + SubDirectory + '''' as MoveTo
FROM #DirTree -- hear I mode files to another folder
) as results
FOR XML PATH('')),
1, 1, '') + ' WITH FILE = 1
MOVE N''file'' TO N''file.mdf'',
MOVE N''file_Log'' TO N''L:\filelog.ldf'',
NOUNLOAD, REPLACE, STATS = 5'
Exec (#sctript)

Related

Bulk insert from file thrown an error Cannot bulk load because the file could not be opened

I'm working with remote Microsoft SQL Server.
After executing following code using DataGrip on Ubuntu I receive an error:
CREATE TABLE #TempNullClass (
id nvarchar(10),
classCode nvarchar(10)
);
BULK INSERT #TempNullClass
FROM '/home/user/Downloads/data1.csv'
WITH (
FIELDTERMINATOR = ',',
ROWTERMINATOR = '0x0a'
);
[S0001][4861] Cannot bulk load because the file "/home/user/Downloads/data1.csv" could not be opened. Operating system error code 3(The system cannot find the path specified.).
File has all of the permissions:
-rwxrwxrwx 1 user user 73399 may 15 15:11 data1.csv
I cannot put the file on remote server.
The file should be placed on the remote server or in a folder that shared to the remote server.
You can use a local file, the file path needs to be relative to the server, so a file on another computer needs to be a UNC path, a server local path does not have to be a share (security concern and one more thing you need to configure and monitor).
I have to loop over import files sometimes like all other DBA's do. I solve that like this.
You can dump it in a SQL Job and schedule it. Can run every second, or every day, just depends what your business requirements are. Here is a sample of one of my scripts:
SET NOCOUNT ON
DECLARE #BackupDirectory SYSNAME = 'G:\'
IF OBJECT_ID('tempdb..#DirTree') IS NOT NULL
DROP TABLE #DirTree
CREATE TABLE #DirTree (
Id int identity(1,1),
SubDirectory nvarchar(255),
Depth smallint,
FileFlag bit,
ParentDirectoryID int
)
INSERT INTO #DirTree (SubDirectory, Depth, FileFlag)
EXEC master..xp_dirtree #BackupDirectory, 10, 1
UPDATE #DirTree
SET ParentDirectoryID = (
SELECT MAX(Id) FROM #DirTree d2
WHERE Depth = d.Depth - 1 AND d2.Id < d.Id
)
FROM #DirTree d
DECLARE
#ID INT,
#BackupFile VARCHAR(MAX),
#Depth TINYINT,
#FileFlag BIT,
#ParentDirectoryID INT,
#wkSubParentDirectoryID INT,
#wkSubDirectory VARCHAR(MAX)
if OBJECT_ID('dbo.ImportFiles')=0
create table dbo.ImportFiles
(
FileNamePath VARCHAR(MAX)
)
else
truncate table dbo.ImportFiles
DECLARE FileCursor CURSOR LOCAL FORWARD_ONLY FOR
SELECT * FROM #DirTree WHERE FileFlag = 1
OPEN FileCursor
FETCH NEXT FROM FileCursor INTO
#ID,
#BackupFile,
#Depth,
#FileFlag,
#ParentDirectoryID
SET #wkSubParentDirectoryID = #ParentDirectoryID
WHILE ##FETCH_STATUS = 0
BEGIN
--loop to generate path in reverse, starting with backup file then prefixing subfolders in a loop
WHILE #wkSubParentDirectoryID IS NOT NULL
BEGIN
SELECT #wkSubDirectory = SubDirectory, #wkSubParentDirectoryID = ParentDirectoryID
FROM #DirTree
WHERE ID = #wkSubParentDirectoryID
SELECT #BackupFile = #wkSubDirectory + '\' + #BackupFile
END
--no more subfolders in loop so now prefix the root backup folder
SELECT #BackupFile = #BackupDirectory + #BackupFile
INSERT INTO ImportFiles (FileNamePath) VALUES(#BackupFile)
FETCH NEXT FROM FileCursor INTO
#ID,
#BackupFile,
#Depth,
#FileFlag,
#ParentDirectoryID
SET #wkSubParentDirectoryID = #ParentDirectoryID
END
CLOSE FileCursor
DEALLOCATE FileCursor
SET NOCOUNT ON
print 'loading files from file table'
-- =============================================
-- Declare and using a READ_ONLY cursor
-- =============================================
DECLARE eoddata_cursor CURSOR
READ_ONLY
FOR SELECT FileNamePath FROM ImportFiles
where FileNamePath like '%.csv'
DECLARE #name nvarchar(4000), #sql nvarchar(2000), #rows int
OPEN eoddata_cursor
FETCH NEXT FROM eoddata_cursor INTO #name
WHILE (##fetch_status <> -1)
BEGIN
IF (##fetch_status <> -2)
BEGIN
print 'file '+#name
begin try
truncate table [Tickers].[DataClient]
set #sql = '
bulk insert [Tickers].[DataClient]
from N'''+#name+'''
with (
FIELDTERMINATOR ='','',
ROWTERMINATOR =''\n'' ,
FIRSTROW = 2,
CODEPAGE = ''RAW'',
MAXERRORS = 2000000,
ERRORFILE = '''+#name+'.err''
)'
insert into [Tickers].[DataClient]
exec(#sql)
exec [Tickers].[MoveDataClientData] #filename = #name, #rows = #rows output
delete from ImportFiles where FileNamePath = #name
print 'loaded :'+str(#rows)
end try
begin catch
print 'error:'+ error_message()
end catch
END
FETCH NEXT FROM eoddata_cursor INTO #name
END
CLOSE eoddata_cursor
DEALLOCATE eoddata_cursor
GO

Searching for records in Microsoft SQL Server

Is it possible to search for records in Microsoft SQL Server manager?
I mean something like in VS pressing Ctrl-F and searching by word?
There is no way in searching for objects in SQL Server management studio. There are views and tables that can be used to search for objects like:
SELECT * FROM sys.tables WHERE name LIKE '%...%'
To search for tables.
If you mean, searching for data within a table, I need to learn T-SQL.
There are non-free 3rd party tools (ex. Redgate's SQL Search), but I use the proc below. You have to create the proc in every [master] database so it's available server-wide, but you can pass in a search term and an optional database name (otherwise it searches through object definitions in all databases):
USE [master]
GO
/****** Object: StoredProcedure [dbo].[sp_FindTextOnServer] Script Date: 10/6/2017 3:39:19 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
/*-----------------------------------------------------------
This procedure will search all or a specified database objects
for the supplied text and return a table with the values
Examples:
EXEC sp_FindTextOnServer 'Billing', 'fcsCore'
EXEC sp_FindTextOnServer 'vJurisdiction'
-----------------------------------------------------------*/
ALTER PROCEDURE [dbo].[sp_FindTextOnServer]
#text VARCHAR(40),
#searchDB VARCHAR(100) = NULL
AS
DECLARE #DisplayText VARCHAR(100),
#sSql VARCHAR(1000),
#line VARCHAR(300),
#char CHAR,
#lineNo INTEGER,
#counter INTEGER,
#AddedRecord BIT,
#dbObjectType VARCHAR(100),
#dbObject VARCHAR(100),
#ObjectBody VARCHAR(7000),
#dbName VARCHAR(100)
SET #DisplayText = #Text
SET #text = '%' + #text + '%'
SET #AddedRecord = 0
CREATE TABLE #SearchResults
(
DBName VARCHAR(100) NOT NULL,
ObjectType VARCHAR(100) NOT NULL,
ObjectName VARCHAR(100) NOT NULL,
Line INT NOT NULL,
Reference VARCHAR(7000) NOT NULL
)
CREATE TABLE #tempSysComments
(
DBName VARCHAR(100),
DBObjectType VARCHAR(100),
DBObject VARCHAR(100),
TextString text
)
--Populate a table with the search results from all databases on the server that include the searched text
SET #sSql = 'USE [?]
SELECT
''?'' AS DBName,
LOWER(REPLACE(o.type_desc, ''_'', '' '')) + '' ('' + RTRIM(type) + '')'' AS DBObjectType,
OBJECT_NAME(sm.object_id) AS DBObject,
CAST(sm.definition AS VARCHAR(7000)) AS TextString
FROM sys.sql_modules AS sm
JOIN sys.objects AS o
ON sm.object_id = o.object_id
WHERE CAST(sm.definition AS VARCHAR(7000)) LIKE ''' + #text + '''
ORDER BY o.type_desc, OBJECT_NAME(sm.object_id)'
IF (#searchDB IS NULL)
BEGIN
INSERT INTO #tempSysComments
EXEC sp_MSFOREachDB #sSql
END
ELSE
BEGIN
SET #sSql = REPLACE(#sSql, '?', #searchDB)
INSERT INTO #tempSysComments
EXEC (#sSql)
END
DECLARE codeCursor CURSOR
FOR
SELECT DBName, DBObjectType, DBObject, TextString
FROM #tempSysComments
WHERE DBName IS NOT NULL
OPEN codeCursor
FETCH NEXT FROM codeCursor INTO #dbName, #dbObjectType, #dbObject, #ObjectBody
IF ##FETCH_STATUS <> 0
BEGIN
PRINT 'Text ''' + #DisplayText + ''' was not found in objects on server ' + ##SERVERNAME
-- Close and release code cursor.
CLOSE codeCursor
DEALLOCATE codeCursor
RETURN
END
-- Search each object within code cursor.
WHILE ##FETCH_STATUS = 0
BEGIN
SET #lineNo = 0
SET #counter = 1
-- Process each line.
WHILE (#counter <> Len(#ObjectBody))
BEGIN
SET #char = SUBSTRING(#ObjectBody, #counter,1)
-- Check for line breaks.
IF (#char = CHAR(13))
BEGIN
SET #lineNo = #lineNo + 1
-- Check if we found the specified text.
IF (PATINDEX(#text, #line) <> 0)
BEGIN
SET #AddedRecord = 1
INSERT #SearchResults
SELECT #dbName, #dbObjectType, #dbObject, #lineNo, LEFT(RTRIM(LTRIM(#line)), 7000)
END
SET #line = ''
END
SET #line = #line + #char
SET #counter = #counter + 1
END
IF (#AddedRecord = 0)
INSERT #SearchResults
SELECT #dbName, #dbObjectType, #dbObject, 0, SUBSTRING(#ObjectBody, 1, 7000)
SET #AddedRecord = 0
FETCH NEXT FROM codeCursor INTO #dbName, #dbObjectType, #dbObject, #ObjectBody
END
-- Close and release cursor.
CLOSE codeCursor
DEALLOCATE codeCursor
-- Return the info.
SELECT DISTINCT DBName, ObjectType, ObjectName, Line, RTRIM(LTRIM(REPLACE(REPLACE(Reference, CHAR(9), ' '), CHAR(13)+CHAR(10), ' '))) AS Reference
FROM #SearchResults
ORDER BY DBName, ObjectType, ObjectName, Line
-- Cleanup.
DROP TABLE #SearchResults
DROP TABLE #tempSysComments
RETURN

SQL script to "copy" a database

I want to write a SQL script that will copy a database on the same server. I could do a backup/restore, but I think it might be faster to just "copy" somehow. Does anyone know if this is possible? Is there a way to write a script that will just detach, copy the file on the HD, and then reattach both copies?
#Tony the Lion:
Hi - I experienced some problems using your script, so I came up with a hybrid of your script and this post:
link
USE master;
GO
-- the original database (use 'SET #DB = NULL' to disable backup)
DECLARE #SourceDatabaseName varchar(200)
DECLARE #SourceDatabaseLogicalName varchar(200)
DECLARE #SourceDatabaseLogicalNameForLog varchar(200)
DECLARE #query varchar(2000)
DECLARE #DataFile varchar(2000)
DECLARE #LogFile varchar(2000)
DECLARE #BackupFile varchar(2000)
DECLARE #TargetDatabaseName varchar(200)
DECLARE #TargetDatbaseFolder varchar(2000)
-- ****************************************************************
SET #SourceDatabaseName = '[Source.DB]' -- Name of the source database
SET #SourceDatabaseLogicalName = 'Source_DB' -- Logical name of the DB ( check DB properties / Files tab )
SET #SourceDatabaseLogicalNameForLog = 'Source_DB_log' -- Logical name of the DB ( check DB properties / Files tab )
SET #BackupFile = 'C:\Temp\backup.dat' -- FileName of the backup file
SET #TargetDatabaseName = 'TargetDBName' -- Name of the target database
SET #TargetDatbaseFolder = 'C:\Temp\'
-- ****************************************************************
SET #DataFile = #TargetDatbaseFolder + #TargetDatabaseName + '.mdf';
SET #LogFile = #TargetDatbaseFolder + #TargetDatabaseName + '.ldf';
-- Backup the #SourceDatabase to #BackupFile location
IF #SourceDatabaseName IS NOT NULL
BEGIN
SET #query = 'BACKUP DATABASE ' + #SourceDatabaseName + ' TO DISK = ' + QUOTENAME(#BackupFile,'''')
PRINT 'Executing query : ' + #query;
EXEC (#query)
END
PRINT 'OK!';
-- Drop #TargetDatabaseName if exists
IF EXISTS(SELECT * FROM sysdatabases WHERE name = #TargetDatabaseName)
BEGIN
SET #query = 'DROP DATABASE ' + #TargetDatabaseName
PRINT 'Executing query : ' + #query;
EXEC (#query)
END
PRINT 'OK!'
-- Restore database from #BackupFile into #DataFile and #LogFile
SET #query = 'RESTORE DATABASE ' + #TargetDatabaseName + ' FROM DISK = ' + QUOTENAME(#BackupFile,'''')
SET #query = #query + ' WITH MOVE ' + QUOTENAME(#SourceDatabaseLogicalName,'''') + ' TO ' + QUOTENAME(#DataFile ,'''')
SET #query = #query + ' , MOVE ' + QUOTENAME(#SourceDatabaseLogicalNameForLog,'''') + ' TO ' + QUOTENAME(#LogFile,'''')
PRINT 'Executing query : ' + #query
EXEC (#query)
PRINT 'OK!'
CREATE DATABASE mydatabase_copy AS COPY OF mydatabase;
This will work on an AZURE Database, and will achieve the same end result. Two attached databases - the original and the copied one.
Source for the script that copies a database.
USE master;
DECLARE
#SourceDatabaseName AS SYSNAME = '<SourceDB>',
#TargetDatabaseName AS SYSNAME = '<TargetDB>'
-- ============================================
-- Define path where backup will be saved
-- ============================================
IF NOT EXISTS (SELECT 1 FROM sys.databases WHERE name = #SourceDatabaseName)
RAISERROR ('Variable #SourceDatabaseName is not set correctly !', 20, 1) WITH LOG
DECLARE #SourceBackupFilePath varchar(2000)
SELECT #SourceBackupFilePath = BMF.physical_device_name
FROM
msdb.dbo.backupset B
JOIN msdb.dbo.backupmediafamily BMF ON B.media_set_id = BMF.media_set_id
WHERE B.database_name = #SourceDatabaseName
ORDER BY B.backup_finish_date DESC
SET #SourceBackupFilePath = REPLACE(#SourceBackupFilePath, '.bak', '_clone.bak')
-- ============================================
-- Backup source database
-- ============================================
DECLARE #Sql NVARCHAR(MAX)
SET #Sql = 'BACKUP DATABASE #SourceDatabaseName TO DISK = ''#SourceBackupFilePath'''
SET #Sql = REPLACE(#Sql, '#SourceDatabaseName', #SourceDatabaseName)
SET #Sql = REPLACE(#Sql, '#SourceBackupFilePath', #SourceBackupFilePath)
SELECT 'Performing backup...', #Sql as ExecutedSql
EXEC (#Sql)
-- ============================================
-- Automatically compose database files (.mdf and .ldf) paths
-- ============================================
DECLARE
#LogicalDataFileName as NVARCHAR(MAX)
, #LogicalLogFileName as NVARCHAR(MAX)
, #TargetDataFilePath as NVARCHAR(MAX)
, #TargetLogFilePath as NVARCHAR(MAX)
SELECT
#LogicalDataFileName = name,
#TargetDataFilePath = SUBSTRING(physical_name,1,LEN(physical_name)-CHARINDEX('\',REVERSE(physical_name))) + '\' + #TargetDatabaseName + '.mdf'
FROM sys.master_files
WHERE
database_id = DB_ID(#SourceDatabaseName)
AND type = 0 -- datafile file
SELECT
#LogicalLogFileName = name,
#TargetLogFilePath = SUBSTRING(physical_name,1,LEN(physical_name)-CHARINDEX('\',REVERSE(physical_name))) + '\' + #TargetDatabaseName + '.ldf'
FROM sys.master_files
WHERE
database_id = DB_ID(#SourceDatabaseName)
AND type = 1 -- log file
-- ============================================
-- Restore target database
-- ============================================
IF EXISTS (SELECT 1 FROM sys.databases WHERE name = #TargetDatabaseName)
RAISERROR ('A database with the same name already exists!', 20, 1) WITH LOG
SET #Sql = 'RESTORE DATABASE #TargetDatabaseName
FROM DISK = ''#SourceBackupFilePath''
WITH MOVE ''#LogicalDataFileName'' TO ''#TargetDataFilePath'',
MOVE ''#LogicalLogFileName'' TO ''#TargetLogFilePath'''
SET #Sql = REPLACE(#Sql, '#TargetDatabaseName', #TargetDatabaseName)
SET #Sql = REPLACE(#Sql, '#SourceBackupFilePath', #SourceBackupFilePath)
SET #Sql = REPLACE(#Sql, '#LogicalDataFileName', #LogicalDataFileName)
SET #Sql = REPLACE(#Sql, '#TargetDataFilePath', #TargetDataFilePath)
SET #Sql = REPLACE(#Sql, '#LogicalLogFileName', #LogicalLogFileName)
SET #Sql = REPLACE(#Sql, '#TargetLogFilePath', #TargetLogFilePath)
SELECT 'Restoring...', #Sql as ExecutedSql
EXEC (#Sql)
Try this:
USE master
GO
-- the original database (use 'SET #DB = NULL' to disable backup)
DECLARE #DB varchar(200)
SET #DB = 'PcTopp'
-- the backup filename
DECLARE #BackupFile varchar(2000)
SET #BackupFile = 'c:\pctopp\sqlserver\backup.dat'
-- the new database name
DECLARE #TestDB varchar(200)
SET #TestDB = 'TestDB'
-- the new database files without .mdf/.ldf
DECLARE #RestoreFile varchar(2000)
SET #RestoreFile = 'c:\pctopp\sqlserver\backup'
-- ****************************************************************
-- no change below this line
-- ****************************************************************
DECLARE #query varchar(2000)
DECLARE #DataFile varchar(2000)
SET #DataFile = #RestoreFile + '.mdf'
DECLARE #LogFile varchar(2000)
SET #LogFile = #RestoreFile + '.ldf'
IF #DB IS NOT NULL
BEGIN
SET #query = 'BACKUP DATABASE ' + #DB + ' TO DISK = ' + QUOTENAME(#BackupFile, '''')
EXEC (#query)
END
-- RESTORE FILELISTONLY FROM DISK = 'C:\temp\backup.dat'
-- RESTORE HEADERONLY FROM DISK = 'C:\temp\backup.dat'
-- RESTORE LABELONLY FROM DISK = 'C:\temp\backup.dat'
-- RESTORE VERIFYONLY FROM DISK = 'C:\temp\backup.dat'
IF EXISTS(SELECT * FROM sysdatabases WHERE name = #TestDB)
BEGIN
SET #query = 'DROP DATABASE ' + #TestDB
EXEC (#query)
END
RESTORE HEADERONLY FROM DISK = #BackupFile
DECLARE #File int
SET #File = ##ROWCOUNT
DECLARE #Data varchar(500)
DECLARE #Log varchar(500)
SET #query = 'RESTORE FILELISTONLY FROM DISK = ' + QUOTENAME(#BackupFile , '''')
CREATE TABLE #restoretemp
(
LogicalName varchar(500),
PhysicalName varchar(500),
type varchar(10),
FilegroupName varchar(200),
size int,
maxsize bigint
)
INSERT #restoretemp EXEC (#query)
SELECT #Data = LogicalName FROM #restoretemp WHERE type = 'D'
SELECT #Log = LogicalName FROM #restoretemp WHERE type = 'L'
PRINT #Data
PRINT #Log
TRUNCATE TABLE #restoretemp
DROP TABLE #restoretemp
IF #File > 0
BEGIN
SET #query = 'RESTORE DATABASE ' + #TestDB + ' FROM DISK = ' + QUOTENAME(#BackupFile, '''') +
' WITH MOVE ' + QUOTENAME(#Data, '''') + ' TO ' + QUOTENAME(#DataFile, '''') + ', MOVE ' +
QUOTENAME(#Log, '''') + ' TO ' + QUOTENAME(#LogFile, '''') + ', FILE = ' + CONVERT(varchar, #File)
EXEC (#query)
END
GO
Got it from here
Is there a way to write a script that will just detach, copy the file on the HD, and then reattach both copies?
Yes. For detaching and attaching you can use sp_detach_db and sp_attach_db. For copying the files, you can use xp_cmdshell and xcopy.
Still, I think the backup-and-restore approach is easier, since it does not require you to copy the files.
Here is a version of the code Tony posted which works in SQL Server 2005
USE master
GO
-- the original database (use 'SET #DB = NULL' to disable backup)
DECLARE #DB varchar(200)
SET #DB = 'GMSSDB'
-- the backup filename
DECLARE #BackupFile varchar(2000)
SET #BackupFile = 'c:\temp\backup.dat'
-- the new database name
DECLARE #TestDB varchar(200)
SET #TestDB = 'GMSSDBArchive'
-- the new database files without .mdf/.ldf
DECLARE #RestoreFile varchar(2000)
SET #RestoreFile = 'c:\temp\backup'
-- ****************************************************************
-- no change below this line
-- ****************************************************************
DECLARE #query varchar(2000)
DECLARE #DataFile varchar(2000)
SET #DataFile = #RestoreFile + '.mdf'
DECLARE #LogFile varchar(2000)
SET #LogFile = #RestoreFile + '.ldf'
IF #DB IS NOT NULL
BEGIN
SET #query = 'BACKUP DATABASE ' + #DB + ' TO DISK = ' + QUOTENAME(#BackupFile, '''')
EXEC (#query)
END
-- RESTORE FILELISTONLY FROM DISK = 'C:\temp\backup.dat'
-- RESTORE HEADERONLY FROM DISK = 'C:\temp\backup.dat'
-- RESTORE LABELONLY FROM DISK = 'C:\temp\backup.dat'
-- RESTORE VERIFYONLY FROM DISK = 'C:\temp\backup.dat'
IF EXISTS(SELECT * FROM sysdatabases WHERE name = #TestDB)
BEGIN
SET #query = 'DROP DATABASE ' + #TestDB
EXEC (#query)
END
CREATE TABLE #headeronly
(
BackupName nvarchar(128) null,
BackupDescription nvarchar(255) null,
BackupType smallint,
ExpirationDate datetime null,
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
)
--RESTORE HEADERONLY FROM DISK = #BackupFile
SET #query = 'RESTORE HEADERONLY FROM DISK = ' + QUOTENAME(#BackupFile, '''')
INSERT #headeronly exec(#query)
DECLARE #File int
select #File = count(1) from #headeronly
print CONVERT(varchar, #File)
DROP TABLE #headeronly
DECLARE #Data varchar(500)
DECLARE #Log varchar(500)
SET #query = 'RESTORE FILELISTONLY FROM DISK = ' + QUOTENAME(#BackupFile , '''')
--RESTORE FILELISTONLY FROM DISK = 'c:\temp\backup.dat'
CREATE TABLE #restoretemp
(
LogicalName nvarchar(128),
PhysicalName nvarchar(260),
type char(1),
FilegroupName nvarchar(128),
size numeric(20,0),
maxsize numeric(20,0),
FileID bigint,
CreateLSN numeric(25,0),
DropLSN numeric(25,0 )NULL,
UniqueID uniqueidentifier,
ReadOnlyLSN numeric(25,0) NULL,
ReadWriteLSN numeric(25,0) NULL,
BackupSizeInBytes bigint,
SourceBlockSize int,
FileGroupID int,
LogGroupGUID uniqueidentifier NULL,
DifferentialBaseLSN numeric(25,0) NULL,
DifferentialBaseGUID uniqueidentifier,
IsReadOnly bit,
IsPresent bit
)
--select * from EXEC (#query)
INSERT #restoretemp EXEC (#query)
SELECT #Data = LogicalName FROM #restoretemp WHERE type = 'D'
SELECT #Log = LogicalName FROM #restoretemp WHERE type = 'L'
PRINT #Data
PRINT #Log
TRUNCATE TABLE #restoretemp
DROP TABLE #restoretemp
print CONVERT(varchar, #File)
IF #File > 0
BEGIN
SET #query = 'RESTORE DATABASE ' + #TestDB + ' FROM DISK = ' + QUOTENAME(#BackupFile, '''') +
' WITH MOVE ' + QUOTENAME(#Data, '''') + ' TO ' + QUOTENAME(#DataFile, '''') + ', MOVE ' +
QUOTENAME(#Log, '''') + ' TO ' + QUOTENAME(#LogFile, '''') + ', FILE = ' + CONVERT(varchar, #File)
print 'starting restore'
EXEC (#query)
print 'finished restore'
END
GO
I'm not sure, but I think you are looking for this:
BACKUP DATABASE MyDB TO DISK='D:\MyDB.bak'

Restoring multiple database backups in a transaction

I wrote a stored procedure that restores as set of the database backups. It takes two parameters - a source directory and a restore directory. The procedure looks for all .bak files in the source directory (recursively) and restores all the databases.
The stored procedure works as expected, but it has one issue - if I uncomment the try-catch statements, the procedure terminates with the following error:
error_number = 3013
error_severity = 16
error_state = 1
error_message = DATABASE is terminating abnormally.
The weird part is sometimes (it is not consistent) the restore is done even if the error occurs. The procedure:
create proc usp_restore_databases
(
#source_directory varchar(1000),
#restore_directory varchar(1000)
)
as
begin
declare #number_of_backup_files int
-- begin transaction
-- begin try
-- step 0: Initial validation
if(right(#source_directory, 1) <> '\') set #source_directory = #source_directory + '\'
if(right(#restore_directory, 1) <> '\') set #restore_directory = #restore_directory + '\'
-- step 1: Put all the backup files in the specified directory in a table --
declare #backup_files table ( file_path varchar(1000))
declare #dos_command varchar(1000)
set #dos_command = 'dir ' + '"' + #source_directory + '*.bak" /s/b'
/* DEBUG */ print #dos_command
insert into #backup_files(file_path) exec xp_cmdshell #dos_command
delete from #backup_files where file_path IS NULL
select #number_of_backup_files = count(1) from #backup_files
/* DEBUG */ select * from #backup_files
/* DEBUG */ print #number_of_backup_files
-- step 2: restore each backup file --
declare backup_file_cursor cursor for select file_path from #backup_files
open backup_file_cursor
declare #index int; set #index = 0
while(#index < #number_of_backup_files)
begin
declare #backup_file_path varchar(1000)
fetch next from backup_file_cursor into #backup_file_path
/* DEBUG */ print #backup_file_path
-- step 2a: parse the full backup file name to get the DB file name.
declare #db_name varchar(100)
set #db_name = right(#backup_file_path, charindex('\', reverse(#backup_file_path)) -1) -- still has the .bak extension
/* DEBUG */ print #db_name
set #db_name = left(#db_name, charindex('.', #db_name) -1)
/* DEBUG */ print #db_name
set #db_name = lower(#db_name)
/* DEBUG */ print #db_name
-- step 2b: find out the logical names of the mdf and ldf files
declare #mdf_logical_name varchar(100),
#ldf_logical_name varchar(100)
declare #backup_file_contents table
(
LogicalName nvarchar(128),
PhysicalName nvarchar(260),
[Type] char(1),
FileGroupName nvarchar(128),
[Size] numeric(20,0),
[MaxSize] numeric(20,0),
FileID bigint,
CreateLSN numeric(25,0),
DropLSN numeric(25,0) NULL,
UniqueID uniqueidentifier,
ReadOnlyLSN numeric(25,0) NULL,
ReadWriteLSN numeric(25,0) NULL,
BackupSizeInBytes bigint,
SourceBlockSize int,
FileGroupID int,
LogGroupGUID uniqueidentifier NULL,
DifferentialBaseLSN numeric(25,0) NULL,
DifferentialBaseGUID uniqueidentifier,
IsReadOnly bit,
IsPresent bit
)
insert into #backup_file_contents
exec ('restore filelistonly from disk=' + '''' + #backup_file_path + '''')
select #mdf_logical_name = LogicalName from #backup_file_contents where [Type] = 'D'
select #ldf_logical_name = LogicalName from #backup_file_contents where [Type] = 'L'
/* DEBUG */ print #mdf_logical_name + ', ' + #ldf_logical_name
-- step 2c: restore
declare #mdf_file_name varchar(1000),
#ldf_file_name varchar(1000)
set #mdf_file_name = #restore_directory + #db_name + '.mdf'
set #ldf_file_name = #restore_directory + #db_name + '.ldf'
/* DEBUG */ print 'mdf_logical_name = ' + #mdf_logical_name + '|' +
'ldf_logical_name = ' + #ldf_logical_name + '|' +
'db_name = ' + #db_name + '|' +
'backup_file_path = ' + #backup_file_path + '|' +
'restore_directory = ' + #restore_directory + '|' +
'mdf_file_name = ' + #mdf_file_name + '|' +
'ldf_file_name = ' + #ldf_file_name
restore database #db_name from disk = #backup_file_path
with
move #mdf_logical_name to #mdf_file_name,
move #ldf_logical_name to #ldf_file_name
-- step 2d: iterate
set #index = #index + 1
end
close backup_file_cursor
deallocate backup_file_cursor
-- end try
-- begin catch
-- print error_message()
-- rollback transaction
-- return
-- end catch
--
-- commit transaction
end
Does anybody have any ideas why this might be happening?
Another question: is the transaction code useful ? i.e., if there are 2 databases to be restored, will SQL Server undo the restore of one database if the second restore fails?
Essentially, what was happening was that one of the files that needed to be restored had a problem, and the restore process was throwing an error, but the error is not severe enough to abort the proc. That is the reason there is no problem without the try-catch. However, adding the try-catch traps any error with severity greater than 10, and therefore the control flow switches to the catch block which displays the error messages and aborts the proc.
Also, if you are not removing the NULL records from your file list (since its commented out) then with your loop starting at 0 it ends up processing a non-existant file for its last iteration.
Instead of #index=0 instead it should be #index=1
or uncomment out the
delete from #backup_files where file_path IS NULL
Problems I noticed:
Commit Transaction needs to be inside
the BEGIN TRY....END TRY block
Cursor will not get closed or
deallocated if an error is
encountered and control goes to BEGIN
CATCH...END CATCH block
Try this modified code. It will demonstrate that your code is working fine..
ALTER proc usp_restore_databases
(
#source_directory varchar(1000),
#restore_directory varchar(1000)
)
as
begin
declare #number_of_backup_files int
begin transaction
begin try
print 'Entering TRY...'
-- step 0: Initial validation
if(right(#source_directory, 1) <> '\') set #source_directory = #source_directory + '\'
if(right(#restore_directory, 1) <> '\') set #restore_directory = #restore_directory + '\'
-- step 1: Put all the backup files in the specified directory in a table --
declare #backup_files table ( file_path varchar(1000))
declare #dos_command varchar(1000)
set #dos_command = 'dir ' + '"' + #source_directory + '*.bak" /s/b'
/* DEBUG */ print #dos_command
insert into #backup_files(file_path) exec xp_cmdshell #dos_command
--delete from #backup_files where file_path IS NULL
select #number_of_backup_files = count(1) from #backup_files
/* DEBUG */ select * from #backup_files
/* DEBUG */ print #number_of_backup_files
-- step 2: restore each backup file --
declare backup_file_cursor cursor for select file_path from #backup_files
open backup_file_cursor
declare #index int; set #index = 0
while(#index < #number_of_backup_files)
begin
declare #backup_file_path varchar(1000)
fetch next from backup_file_cursor into #backup_file_path
/* DEBUG */ print #backup_file_path
-- step 2a: parse the full backup file name to get the DB file name.
declare #db_name varchar(100)
set #db_name = right(#backup_file_path, charindex('\', reverse(#backup_file_path)) -1) -- still has the .bak extension
/* DEBUG */ print #db_name
set #db_name = left(#db_name, charindex('.', #db_name) -1)
/* DEBUG */ print #db_name
set #db_name = lower(#db_name)
/* DEBUG */ print #db_name
-- step 2b: find out the logical names of the mdf and ldf files
declare #mdf_logical_name varchar(100),
#ldf_logical_name varchar(100)
declare #backup_file_contents table
(
LogicalName nvarchar(128),
PhysicalName nvarchar(260),
[Type] char(1),
FileGroupName nvarchar(128),
[Size] numeric(20,0),
[MaxSize] numeric(20,0),
FileID bigint,
CreateLSN numeric(25,0),
DropLSN numeric(25,0) NULL,
UniqueID uniqueidentifier,
ReadOnlyLSN numeric(25,0) NULL,
ReadWriteLSN numeric(25,0) NULL,
BackupSizeInBytes bigint,
SourceBlockSize int,
FileGroupID int,
LogGroupGUID uniqueidentifier NULL,
DifferentialBaseLSN numeric(25,0) NULL,
DifferentialBaseGUID uniqueidentifier,
IsReadOnly bit,
IsPresent bit
)
insert into #backup_file_contents
exec ('restore filelistonly from disk=' + '''' + #backup_file_path + '''')
select #mdf_logical_name = LogicalName from #backup_file_contents where [Type] = 'D'
select #ldf_logical_name = LogicalName from #backup_file_contents where [Type] = 'L'
/* DEBUG */ print #mdf_logical_name + ', ' + #ldf_logical_name
-- step 2c: restore
declare #mdf_file_name varchar(1000),
#ldf_file_name varchar(1000)
set #mdf_file_name = #restore_directory + #db_name + '.mdf'
set #ldf_file_name = #restore_directory + #db_name + '.ldf'
/* DEBUG */ print 'mdf_logical_name = ' + #mdf_logical_name + '|' +
'ldf_logical_name = ' + #ldf_logical_name + '|' +
'db_name = ' + #db_name + '|' +
'backup_file_path = ' + #backup_file_path + '|' +
'restore_directory = ' + #restore_directory + '|' +
'mdf_file_name = ' + #mdf_file_name + '|' +
'ldf_file_name = ' + #ldf_file_name
print #index
-- restore database #db_name from disk = #backup_file_path
-- with
-- move #mdf_logical_name to #mdf_file_name,
-- move #ldf_logical_name to #ldf_file_name
-- step 2d: iterate
set #index = #index + 1
end
close backup_file_cursor
deallocate backup_file_cursor
end try
begin catch
print 'Entering Catch...'
print error_message()
rollback transaction
return
end catch
commit transaction
end
Raj
The actual problem here is that the try and catch only gives you the last error message 3013 "backup terminating abnormally", but does not give you the lower level error for the reason the 3013 error was triggered.
If you execute a backup command such as with an incorrect databasename, you will get 2 errors.
backup database incorrect_database_name to disk = 'drive:\path\filename.bak'
Msg 911, Level 16, State 11, Line 1
Could not locate entry in sysdatabases for database 'incorrect_database_name'. No entry found with that name. Make sure that the name is entered correctly.
Msg 3013, Level 16, State 1, Line 1
BACKUP DATABASE is terminating abnormally.
If you want to know the actual error for why you backup is failing within a try a catch, the stored procedure is masking it.
Now, on to your question.. what I would do is when a restore succeeds, I would immediately delete or move the .bak to a new location, thereby removing it from the directory you stated in your parameter. Upon a failure, your catch statement can contain a GOTO that takes you back to before the BEGIN TRY and starts executing where it left off because it will not recursively detect the files you have moved from the directory.
RUN_AGAIN:
BEGIN TRY
RECURSIVE DIR FOR FILENAMES
RESTORE DATABASE...
ON SUCCEED, DELETE .BAK FILE
END TRY
BEGIN CATCH
ON FAILURE, MOVE .BAK to A SAFE LOCATION FOR LATER ANALYSIS
GOTO RUN_AGAIN
END CATCH
I'm not saying it is pretty, but it will work. You cannot put a GOTO reference within a TRY/CATCH block, so it has to be outside of it.
Anyway, I just thought I would add my thoughts to this even though the question is old, just to help out others in the same situation.

Microsoft SQL Server, restore a backup of a database with one command

When we copy a database down from production, we make a backup of the database, zip it up and copy the backup down. Then we have to restore using the SQL Server GUI, which involves navigating through several menus and windows. As far as I know, you can not do this with SQL Server's built in stored procedures because you may not know the logical filename of the database (which is required to restore). So doing this via query consists of the following:
RESTORE FILELISTONLY
FROM DISK = 'C:\backup_of_production_database.bak'
GO
The above provides the logical file names from the backup file, you then have to use these logical names in the next query...
RESTORE DATABASE NewDevelopmentDatabase
FROM DISK = 'C:\backup_of_production_database.bak'
WITH MOVE 'YourMDFLogicalName' TO 'C:\mssql\data\DataYourMDFFile.mdf',
MOVE 'YourLDFLogicalName' TO 'C:\mssql\data\DataYourLDFFile.mdf'
As you can see this seems inefficient because you must manually enter the logical file names into the next query.
You can find my solution to this problem as an answer below.
Here is a SQL script that will restore a database with no interaction required.
Just enter your "Source Database" & your "Destination Database" - the script will do the rest :)
SET NOCOUNT ON;
DECLARE
#MySourceDatabase NVarchar(1000),
#MyDestinationDatabase NVarchar(100),
#DeviceFrom NVarchar(1000),
#DeviceTo NVarchar(1000),
#LogicalName NVarchar(1000),
#PhysicalName NVarchar(1000),
#SQL NVarchar(MAX),
#RowsToProcess integer,
#CurrentRow integer,
#Comma NVarchar(25);
--SOURCE DATABASE (DATABASE TO RESTORE)
SET #MySourceDatabase = 'D:\Backups\backup_db.bak';
--DESTINATION DATABASE (DATABASE TO RESTORE TO)
SET #MyDestinationDatabase = 'mydatabase_db';
SELECT #DeviceFrom = SUBSTRING(physical_name, 1,
CHARINDEX(#MyDestinationDatabase + '.mdf',
physical_name) - 1)
FROM master.sys.master_files
WHERE name = #MyDestinationDatabase AND FILE_ID = 1;
SET #SQL = 'RESTORE DATABASE ' + #MyDestinationDatabase + ' FROM DISK = ''' + #MySourceDatabase + ''' WITH ';
SET #CurrentRow = 0;
SET #Comma = ',';
DECLARE #FileList TABLE (
RowID int not null primary key identity(1,1)
,LogicalName NVARCHAR(128)
,PhysicalName NVARCHAR(260)
,Type CHAR(1)
,FileGroupName NVARCHAR(128)
,Size numeric(20,0)
,MaxSize numeric(20,0)
,FileId BIGINT
,CreateLSN numeric(25,0)
,DropLSN numeric(25,0)
,UniqueId uniqueidentifier
,ReadOnlyLSN numeric(25,0)
,ReadWriteLSN numeric(25,0)
,BackupSizeInBytes BIGINT
,SourceBlockSize BIGINT
,FilegroupId BIGINT
,LogGroupGUID uniqueidentifier
,DifferentialBaseLSN numeric(25)
,DifferentialBaseGUID uniqueidentifier
,IsReadOnly BIGINT
,IsPresent BIGINT
,TDEThumbprint VARBINARY(32) -- Remove this line for SQL Server 2005
);
INSERT INTO #FileList
EXEC('RESTORE FILELISTONLY FROM DISK = ''' + #MySourceDatabase + '''')
SET #RowsToProcess = ##RowCount;
WHILE #CurrentRow < #RowsToProcess
BEGIN
SET #CurrentRow= #CurrentRow + 1;
BEGIN
IF #CurrentRow = #RowsToProcess
SET #Comma = ',REPLACE';
END
SELECT #LogicalName = LogicalName,#PhysicalName = PhysicalName FROM #FileList WHERE RowID=#CurrentRow;
SET #PhysicalName = Replace(#PhysicalName,#LogicalName,#MyDestinationDatabase);
SET #SQL = #SQL + 'MOVE ''' + #LogicalName + ''' TO ''' + #PhysicalName + '''' + #Comma + '';
END
--PREVIEW THE GENERATED QUERY
SELECT #SQL;
--EXECUTE THE GENERATED QUERY
--EXEC(#SQL);
It will automatically produce a query like:
RESTORE DATABASE mydatabase_db
FROM DISK = 'D:\Backups\backup_db.bak'
WITH
MOVE 'backup_db'
TO 'C:\Program Files\Microsoft SQL Server\MSSQL10_50.SQLEXPRESS\MSSQL\DATA\mydatabase_db.mdf',
MOVE 'backup_db_log'
TO 'C:\Program Files\Microsoft SQL Server\MSSQL10_50.SQLEXPRESS\MSSQL\DATA\mydatabase_db.LDF',
REPLACE
The solution:
Using various resources I came up with the below stored procedure that lets you cut down this restore into one step. I hope it proves as useful to others as it has been to myself.
ALTER PROCEDURE [dbo].[sp_makedev]
#backupfile sysname,
#newdatabase sysname
AS
BEGIN
DECLARE #fname VARCHAR(200)
DECLARE #dirfile VARCHAR(300)
DECLARE #LogicalName NVARCHAR(128)
DECLARE #PhysicalName NVARCHAR(260)
DECLARE #type CHAR(1)
DECLARE #sql NVARCHAR(1000)
DECLARE #mdfFilePath varchar(1000)
DECLARE #ldfFilePath varchar(1000)
CREATE TABLE #dbfiles(
LogicalName NVARCHAR(128)
,PhysicalName NVARCHAR(260)
,Type CHAR(1)
,FileGroupName NVARCHAR(128)
,Size numeric(20,0)
,MaxSize numeric(20,0)
,FileId INT
,CreateLSN numeric(25,0)
,DropLSN numeric(25,0)
,UniqueId uniqueidentifier
,ReadOnlyLSN numeric(25,0)
,ReadWriteLSN numeric(25,0)
,BackupSizeInBytes INT
,SourceBlockSize INT
,FilegroupId INT
,LogGroupGUID uniqueidentifier
,DifferentialBaseLSN numeric(25)
,DifferentialBaseGUID uniqueidentifier
,IsReadOnly INT
,IsPresent INT
)
set #mdfFilePath = ''c:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\data''
set #ldfFilePath = ''c:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\data''
set #sql = ''RESTORE DATABASE '' + #newdatabase + '' FROM DISK = '''''' + #backupfile + '''''' WITH MOVE ''
DECLARE dbfiles CURSOR FOR
SELECT LogicalName, PhysicalName, [type] FROM #dbfiles
INSERT #dbfiles
EXEC(''RESTORE FILELISTONLY FROM DISK = '''''' + #backupfile + '''''''')
OPEN dbfiles
FETCH NEXT FROM dbfiles INTO #LogicalName, #PhysicalName, #type
WHILE ##FETCH_STATUS = 0
BEGIN
IF #type = ''D''
SET #sql = #sql + '''''''' + #LogicalName + '''''' TO '''''' + #mdfFilePath + ''\'' + #newdatabase + ''.mdf'''', MOVE ''
ELSE IF #type = ''L''
SET #sql = #sql + '''''''' + #LogicalName + '''''' TO '''''' + #ldfFilePath + ''\'' + #newdatabase + ''.ldf''''''
FETCH NEXT FROM dbfiles INTO #LogicalName, #PhysicalName, #type
END
CLOSE dbfiles
DEALLOCATE dbfiles
EXEC(#SQL)
END
I'm sure a few things about this query can be improved, however I already wasted enough time just trying to come to this solution. Regardless I'd love to hear some feedback. I hope that others find this useful!

Resources