SQL Scripted Restore Using With Move - With File & With - sql

I am trying to script a restore of one of our databases and I need to know the correct syntax for using the with file and with move commands
So far I have:
RESTORE DATABASE Test
FROM DISK = 'E:\Test_db_BASELINE.BAK'
WITH FILE = 1
WITH MOVE 'Test_dat' TO 'C:\MSSQL\v2_Data\Test_Data.MDF',
MOVE 'Test_log' TO 'C:\MSSQL\v2_Data\Test_Log.LDF', REPLACE
I get syntax error any ideas?
to put into context I am trying to execute the below to automate a restore where the bak file has a timestamped naming convention:
--==CHECK IF DB EXISTS IF IT DOES DROP IT
USE [master]
IF EXISTS(SELECT * FROM sys.databases where name='SlotLookup')
DROP DATABASE [SlotLookup]
--==START THE RESTORE PROCESS
DECLARE #FileName varchar(255), #PathToBackup varchar(255), #RestoreFilePath varchar(1000)
DECLARE #Files TABLE (subdirectory varchar(255), depth int, [file] int)
SET NOCOUNT ON
--==SET THE FILEPATH
SET #PathToBackup = 'path'
--insert into our memory table using dirtree and a single file level
INSERT INTO #Files
EXEC master.dbo.xp_DirTree #PathToBackup,1,1
SELECT TOP 1
#FileName = [subdirectory]
FROM
#Files
WHERE
-- get where it is a file
[file] = 1
AND
--==FIND THE LOGICAL NAME OF THE BAK FILE FROM THE CHRONILOGICALLY ORDERED LIST
subdirectory LIKE '%.bak'
ORDER BY
-- order descending so newest file will be first by naming convention
subdirectory DESC
IF LEFT(REVERSE(#PathToBackup), 1) != '\'
BEGIN
SET #PathToBackup = #PathToBackup + '\'
END
SET #RestoreFilePath = #PathToBackup + #FileName
SELECT #RestoreFilePath
--===BEGIN THE RESTORE TO THE DESIGNATED SERVER
RESTORE DATABASE [SlotLookup]
FROM DISK = #RestoreFilePath
FILE = 1
WITH MOVE 'SlotLookup' TO 'path\SlotLookup.mdf',
MOVE 'SlotLookup_log' TO 'path\SlotLookup_log.ldf'
get syntax error
Msg 156, Level 15, State 1, Line 48
Incorrect syntax near the keyword 'FILE'.
Msg 319, Level 15, State 1, Line 49

The syntax of a restore backup script with move is like this following:
RESTORE DATABASE [AdventureWorksCopy] FROM DISK = 'c:\mssql\backup\[...]\Backup.bak'
WITH CHECKSUM,
MOVE 'AdventureWorks_Data' TO 'c:\mssql\data\[...]\AdventureWorksCopy_Data.mdf',
MOVE 'AdventureWorks_Log' TO 'c:\mssql\log\[...]\AdventureWorksCopy_Log.ldf',
RECOVERY, REPLACE, STATS = 10;
What syntax error do you get?
Please include it in the questions

Related

Restore Backups automatically using SQL Server

The general context is that, I try to restore a list of backups automatically using the script below. I put all the backups in the same folder, after that I put the names of the backups in a table to retrieve them easily. Finally, I put a cursor that points each time to a backup and restores it. Now when executing script, I get this error:
Msg 3234, Level 16, State 2, Line 82 Logical file
'OP38MLG_db_201903040000_DATA' is not part of database
'OP38MLG_db_201903040000'. Use RESTORE FILELISTONLY to list the
logical file names. Msg 3013, Level 16, State 1, Line 82 RESTORE
DATABASE is terminating abnormally.
When I restore each backups independently, the operation ends successfully But when I try to restore the list I got error.
DECLARE #name VARCHAR(50) -- database name
DECLARE #path VARCHAR(256) -- path for backup files
DECLARE #fileName VARCHAR(256) -- filename for backup
-- specify database backup directory
SET #path = 'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\Backup\'
DECLARE #backuppath NVARCHAR(256) -- path for backup files
DECLARE #datapath VARCHAR(256) -- path for data files
DECLARE #logpath VARCHAR(256) -- path for log files
DECLARE #backupfileName VARCHAR(256) -- filename for backup
DECLARE #datafileName VARCHAR(256) -- filename for database
DECLARE #logfileName VARCHAR(256) -- filename for logfile
DECLARE #logName VARCHAR(256) -- filename for logfile
DECLARE #dataName VARCHAR(256)
-- specify database backup directory
SET #backuppath = 'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\Backup\'
SET #datapath = 'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\DATA\'
SET #logpath = 'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\DATA\'
print 'backup path is ' + #backuppath
print 'data path is ' + #datapath
print 'log path is ' + #logpath
/*Table to hold each backup file name in*/
CREATE TABLE #List(fname varchar(200),depth int, file_ int)
INSERT #List
EXECUTE master.dbo.xp_dirtree #backuppath, 1, 1
SELECT * FROM #List
DECLARE files CURSOR FOR
SELECT fname FROM #List
OPEN files
FETCH NEXT FROM files INTO #name
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #cleanname AS VARCHAR(255)
SET #cleanname = REPLACE(#name, '.BAK', '')
PRINT #cleanname
SET #backupfileName = #backuppath + #name
SET #datafileName = #datapath + #cleanname + '.MDF'
SET #logfileName = #logpath + #cleanname + '_log.LDF'
SET #logName = #cleanname + '_log'
SET #dataName = #cleanname + '_DATA'
print 'backup file is ' + #backupfileName
print 'data file is ' + #datafileName
print 'log file is ' + #logfileName
USE [master]
RESTORE DATABASE #cleanname
FROM DISK = #backupfileName
WITH FILE = 1,
MOVE #dataName TO #datafileName,
MOVE #logName TO #logfileName,
NOUNLOAD, STATS = 5
FETCH NEXT FROM files INTO #name
END
CLOSE files
DEALLOCATE files
DROP TABLE #List
GO
I expect in the output that the backups will be restored and I could see them in the database bar, but the actual output is error that I mentioned above.
SQL Server databases consist of a number of "Logical Files". When you restore a database you need to say where these files should be stored. You are doing that, but your code makes assumptions about the names of these logical files. You are assuming they are FileName_Data and FileName_Log. The error message is telling you that is wrong in this case. If you can run the following query, look in the LogicalName field to see the actual values for this .BAK file.
RESTORE FILELISTONLY FROM DISK = N'C:\MyBackups\OP38MLG_db_201903040000.bak'
Over the years, this has happened to me many times. And it's always a surprise to find out the actual logical file names.

Backup script I've been using with no issues for a while tossing an error on new database

I've been using the code below to drop and create a new backup named (current year database)_daily at midnight to allow my team to test new scripts or updates to our student information system.
It worked all last year, and this year for reasons I can't figure out, the script is tossing an error.
Here is the script:
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 = '[DST18000RD]' -- Name of the source database
SET #SourceDatabaseLogicalName = 'DST18000RD' -- Logical name of the DB ( check DB properties / Files tab )
SET #SourceDatabaseLogicalNameForLog = 'DST18000RD_log' -- Logical name of the DB ( check DB properties / Files tab )
SET #BackupFile = 'F:\Dev_Databases\Temp\backup.dat' -- FileName of the backup file
SET #TargetDatabaseName = 'DST18000RD_Daily' -- Name of the target database
SET #TargetDatbaseFolder = 'F:\Dev_Databases\Temp\'
-- ****************************************************************
SET #DataFile = #TargetDatbaseFolder + #TargetDatabaseName + '.mdf';
SET #LogFile = #TargetDatbaseFolder + #TargetDatabaseName + '.ldf';
-- Disconnect any users using #TargetDatabaseName
USE [master];
DECLARE #kill varchar(8000) = '';
SELECT #kill = #kill + 'kill ' + CONVERT(varchar(5), session_id) + ';'
FROM sys.dm_exec_sessions
WHERE database_id = db_id('DST18000RD_Daily')
EXEC(#kill);
-- 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!'
The script is not mine, I put together two scripts to get me what I needed. Our old database DST17000RD, this script still works flawlessly. On the new database DST18000RD, I get this error:
Executing query : BACKUP DATABASE [DST18000RD] TO DISK = 'F:\Dev_Databases\Temp\backup.dat'
Processed 1209552 pages for database 'DST18000RD', file 'DST18000RD' on file 23.
Processed 2 pages for database 'DST18000RD', file 'DST18000RD_log' on file 23.
BACKUP DATABASE successfully processed 1209554 pages in 139.942 seconds (67.525 MB/sec).
OK!
OK!
Executing query : RESTORE DATABASE DST18000RD_Daily FROM DISK = 'F:\Dev_Databases\Temp\backup.dat' WITH MOVE 'DST18000RD' TO 'F:\Dev_Databases\Temp\DST18000RD_Daily.mdf' , MOVE 'DST18000RD_log' TO 'F:\Dev_Databases\Temp\DST18000RD_Daily.ldf'
Msg 3234, Level 16, State 2, Line 3
Logical file 'DST18000RD' is not part of database 'DST18000RD_Daily'. Use RESTORE FILELISTONLY to list the logical file names.
Msg 3013, Level 16, State 1, Line 3
RESTORE DATABASE is terminating abnormally.
OK!
Some things to note that may just be me barking up the wrong tree. DST17000RD database is compatibility level SQL Server 2012 (110) and the DST18000RD database is SQL Server 2017 (140). The server was upgraded and migrated a couple months ago before the new database was created.
Any help is appreciated. From what I can tell, I feel like the script is not renaming the MDF and LDF files before it tries to copy them for the *_daily database? Honestly I'm not sure. I'm a pretend DBA, self taught on an as needed basis. Thank you in advance for your help!
The error is telling you got the logical name of the db file wrong:
SET #SourceDatabaseLogicalName = 'DST18000RD' -- Logical name of the DB ( check DB properties / Files tab )
and to run:
RESTORE FILELIST ONLY FROM DISK = 'F:\Dev_Databases\Temp\backup.dat'
To see the correct logical file names.
The issue is you are trying to change the file logical name during the database restore, which is not possible even if you use the MOVE clause.
The MOVE clause allows you to change the location and the names of the physical files but does not do anything for the logical names.
Fix
You will have to use the existing logical names for your database, but once you have restored the database then use ALTER DATABASE command to change the logical names of your files using the following command:
USE [master];
GO
ALTER DATABASE [DST18000RD]
MODIFY FILE ( NAME = DST17000RD , NEWNAME = DST18000RD );
GO

Scripted Restore Using xp_DirTree For Transient Logical BAK File Name SQL Server

Hi I am trying to restore a DB from one server to another where the logical name of the .bak file changes daily with a new timestamp, I have so far found success in determining this name using the following SQL script provided by Jeff Moden here: http://www.sqlservercentral.com/Forums/Topic1200360-391-1.aspx
--===== Create a holding table for the file names
CREATE TABLE #File
(
FileName SYSNAME,
Depth TINYINT,
IsFile TINYINT
)
;
--===== Capture the names in the desired directory
-- (Change "C:\Temp" to the directory of your choice)
INSERT INTO #File
(FileName, Depth, IsFile)
EXEC xp_DirTree '\\filepath\',1,1
;
--===== Find the latest file using the "constant" characters
-- in the file name and the ISO style date.
SELECT TOP 1
FileName
FROM #File
WHERE IsFile = 1
AND FileName LIKE '%.bak' ESCAPE '_'
ORDER BY FileName DESC
;
DROP TABLE #File
My question is now how do I use this as the basis of a scripted restore operation? any help would be very much appreciated!
I have found success by extending the above to cache the directory path and ordered bak files chronologically to determine which to use, then combined the restore operation, with move for logs.
--==CHECK IF DB EXISTS IF IT DOES DROP IT
USE [master]
IF EXISTS(SELECT * FROM sys.databases where name='insert db name')
DROP DATABASE [insert db name]
--==START THE RESTORE PROCESS
DECLARE #FileName varchar(255), #PathToBackup varchar(255), #RestoreFilePath varchar(1000)
DECLARE #Files TABLE (subdirectory varchar(255), depth int, [file] int)
SET NOCOUNT ON
--==SET THE FILEPATH
SET #PathToBackup = '\\insert path to back up'
--insert into memory table using dirtree at a single file level
INSERT INTO #Files
EXEC master.dbo.xp_DirTree #PathToBackup,1,1
SELECT TOP 1
#FileName = [subdirectory]
FROM
#Files
WHERE
-- get where it is a file
[file] = 1
AND
--==FIND THE LOGICAL NAME OF THE BAK FILE FROM THE CHRONILOGICALLY ORDERED LIST
subdirectory LIKE '%.bak'
ORDER BY
-- order descending so newest file will be first by naming convention
subdirectory DESC
IF LEFT(REVERSE(#PathToBackup), 1) != '\'
BEGIN
SET #PathToBackup = #PathToBackup + '\'
END
SET #RestoreFilePath = #PathToBackup + #FileName
--Grab the file path to restore from
SELECT #RestoreFilePath
--BEGIN THE RESTORE TO THE DESIGNATED SERVER
RESTORE DATABASE [insert name of database to restore]
FROM DISK = #RestoreFilePath
WITH
FILE = 1,
--Create transactional log files on target
MOVE 'mdf_file_name' TO 'file_path\file.mdf',
MOVE 'log_file_name' TO 'file_path\file.ldf', REPLACE;
Here is a script that I have partly written and partly collected.
Features include:
Imports exactly one backup file that has any file name with .bak
ending. If there are more files, then imports only one without any
errors.
If no files exist, gives an error.
Kicks users out from the database.
Deletes the backup file
DECLARE #DBName nvarchar(255), #FileName nvarchar(255), #PathToBackup nvarchar(255), #RestoreFilePath nvarchar(1000)
DECLARE #Files TABLE (subdirectory nvarchar(255), depth int, [file] int)
SET XACT_ABORT, NOCOUNT ON
SET #PathToBackup = N'I:\Folder'
-- insert into our memory table using dirtree and a single file level
INSERT INTO #Files
EXEC master.dbo.xp_DirTree #PathToBackup,1,1
SELECT
#FileName = [subdirectory]
FROM
#Files
WHERE
-- get where it is a file
[file] = 1
AND
subdirectory LIKE N'%.bak'
ORDER BY
-- order descending so newest file will be first by naming convention
subdirectory DESC
IF LEFT(REVERSE(#PathToBackup), 1) != N'\'
BEGIN
SET #PathToBackup = #PathToBackup + N'\'
END
SET #RestoreFilePath = #PathToBackup + #FileName
SET #DBName = LEFT(#FileName, LEN(#FileName)-4)
-- SELECT 'Replace AdventureWorks2016CTP3 in this script with #DBName'
SELECT #RestoreFilePath
BEGIN TRY
-- You can try to check if this command works "already":
-- ALTER DATABASE [#DBName] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
ALTER DATABASE [AdventureWorks2016CTP3] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
-- You can try to check if this command works "already":
-- RESTORE DATABASE [#DBName]
RESTORE DATABASE [AdventureWorks2016CTP3]
FROM DISK = #RestoreFilePath
WITH FILE = 1, NOUNLOAD, REPLACE, STATS = 10
END TRY
BEGIN CATCH
-- You can try to check if this command works "already":
-- ALTER DATABASE [#DBName] SET MULTI_USER;
ALTER DATABASE [AdventureWorks2016CTP3] SET MULTI_USER;
; THROW
END CATCH
-- You can try to check if this command works "already":
-- ALTER DATABASE [#DBName] SET MULTI_USER;
ALTER DATABASE [AdventureWorks2016CTP3] SET MULTI_USER;
-- This script is especially for the case where you replication from one location to another using backup and restore.
-- Typically you don't need transaction log backups as all changes will be wiped out on next transfer.
-- You can try to check if this command works "already":
-- ALTER DATABASE [#DBName] SET RECOVERY SIMPLE;
ALTER DATABASE [AdventureWorks2016CTP3] SET RECOVERY SIMPLE;
-- Delete file(s)
-- NOTE: This works only if you give deletion permissions as defined in https://learn.microsoft.com/en-us/sql/database-engine/configure-windows/xp-cmdshell-server-configuration-option?view=sql-server-2017
-- EXAMPLE: exec xp_cmdshell 'del "I:\Directory\AdventureWorks2016CTP3___.bak"'
-- exec xp_cmdshell 'del "' + '#PathToBackup + ''\'' + #FileName + ''"''
DECLARE #cmd NVARCHAR(MAX) = 'xp_cmdshell ''del "' + #PathToBackup + #FileName + '"''';
-- SELECT #cmd
EXEC (#cmd)

How can I create a database and USE statements in one script?

Consider the following script:
DECLARE #path varchar(MAX)
DECLARE #script varchar(MAX)
SET #path = (SELECT physical_name FROM sys.master_files where name = 'master');
SET #path = REPLACE(#path, 'master.mdf', '');
SELECT #path;
SET #script =
'CREATE DATABASE test
ON PRIMARY
(NAME = test_primary,
FILENAME = ''' + #path + 'test_primary.mdf'',
SIZE = 10MB,
FILEGROWTH = 10MB)';
exec(#script);
USE test
When I try to run it all at once I get an error:
Msg 911, Level 16, State 1, Line 31
Database 'test' does not exist. Make sure that the name is entered correctly.
If I first run exec and then separately run USE it all goes fine.
The question is, how can I work-around it, so that it'd be possible to run the whole script at once with no errors?
SQL Server compiles the code for one batch at a time. When your code is compiled the database does not exist.
Add a batch separator before use test.
DECLARE #path varchar(MAX)
DECLARE #script varchar(MAX)
SET #path = (SELECT physical_name FROM sys.master_files where name = 'master');
SET #path = REPLACE(#path, 'master.mdf', '');
SELECT #path;
SET #script =
'CREATE DATABASE test
ON PRIMARY
(NAME = test_primary,
FILENAME = ''' + #path + 'test_primary.mdf'',
SIZE = 10MB,
FILEGROWTH = 10MB)';
exec(#script);
GO
USE test
If you are executing the statements from within one of the SQL Server query tools (e.g. enterprise manager, management studio or sqlcmd), then insert the statement GO prior to the USE test command. This separates the commands into separate batches. If you are executing the script through one of the programmatic clients, then you must execute the batches separately by splitting up the script.
Souldn't USE be at the top of the script?
USE selects the correct database for you. Then run the script on that database

A file activation error occurred. The physical file name 'N#filename

Below t-sql code compiles fine. But when I run it like
exec [SP_ATTACH_NW] N'C:\myfolder' I get
Msg 5105, Level 16, State 2, Procedure SP_ATTACH_NW, Line 14
A file activation error occurred. The physical file name 'N#mdfFileName' may be incorrect. Diagnose and correct additional errors, and retry the operation.
USE master
GO
SET ANSI_NULLS ON
GO
CREATE PROCEDURE [dbo].[SP_ATTACH_NW] (
#DestFolder varchar(255)
)
AS
BEGIN
SET NOCOUNT ON;
Declare #mdfFileName varchar(255)
Declare #ldfFileName varchar(255)
set #mdfFileName = #DestFolder + '\northwnd.mdf'
set #ldfFileName = #DestFolder + '\northwnd.ldf'
CREATE DATABASE [Northwind] ON
( FILENAME = N#mdfFileName ),
( FILENAME = N#ldfFileName )
FOR ATTACH
END
Pls advise. thanks
You can't have variables in the filename arguments of CREATE DATABASE (MSDN doesn't show #vars in the syntax)
The code above is literally looking for the constant "N#mdfFileName" as a filename.
You'd need dynamic SQL to build a string in, say, #MyBuiltSQL then run EXEC(#MyBuiltSQL)
Note: The "N" prefix here would not make #mdfFileName nvarchar anyway