Restore SQL Database with Replace option - sql-server

I am trying to understand the REPLACE option from various sources and have not clarified myself. I am using SQL Server 2014 version.
What is the difference between below 2 queries? Both are completing without any error. With and Without Replace.
USE [master]
RESTORE DATABASE [Test] FROM DISK = N'C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\Backup\MyDatabase.bak'
WITH FILE = 3, NOUNLOAD, STATS = 5
GO
Vs
USE [master]
RESTORE DATABASE [Test] FROM DISK = N'C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\Backup\MyDatabase.bak'
WITH REPLACE, FILE = 3, NOUNLOAD, STATS = 5
GO
When I try to Replace with the .mdf and .ldf of an existing database (Test2), I get an error. My understanding so far is that I can restore the backup of [Test1] db with the name [Test2] (This already exists in my server and Test1 DB also exists).
I got the statement for REPLACE "Restoring over an existing database with a backup taken of another database." from one of Microsoft Link.
USE [master]
RESTORE DATABASE [Test2] FROM DISK = N'C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\Backup\MyDatabase.bak'
WITH REPLACE, FILE = 3, NOUNLOAD, STATS = 5
GO
Error:
Msg 1834, Level 16, State 1, Line 2
The file 'C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\DATA\test1.mdf' cannot be overwritten. It is being used by database 'test1'.
Msg 3156, Level 16, State 4, Line 2
'test1' cannot be restored to 'C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\DATA\test1.mdf'. Use WITH MOVE to identify a valid location for the file.
Msg 1834, Level 16, State 1, Line 2
What is the purpose of the REPLACE option and what it replaces behind the scene. Can any one please explain with any example?

The purpose of the REPLACE option is to allow you to overwrite a database name (clobber an existing database with a different name from the backup). Your errors are caused by you trying to overwrite the files of another database from the one you asked to restore over. So restoring over Test2 database but clobbering Test1 database files (Test1.MDB and Test1.LDB). If you don't use the MOVE clause then the RESTORE command will use the same filenames as the BACKUP. Probably not a good idea otherwise things are going to get pretty confusing. (Test2 database pointing to Test1.mdb and Test1.ldb)
My script below demonstrates the issue.
I create database Test1.
I Back it up.
I try to restore it to Test2 using REPLACE. It fails, because Test1.mdb and Test1.ldb are being used by the Test1 database.
I drop the Test1 database.
I try to restore it to Test2 using REPLACE and it now works. (But is using Test1.mdb and Test1.ldb).
CREATE DATABASE [Test1]
ON PRIMARY
( NAME = N'Test1', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\DATA\Test1.mdf' , SIZE = 8192KB , FILEGROWTH = 65536KB )
LOG ON
( NAME = N'Test1_log', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\DATA\Test1_log.ldf' , SIZE = 8192KB , FILEGROWTH = 65536KB )
GO
BACKUP DATABASE [Test1] TO DISK = N'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\Backup\Test1.bak' WITH COPY_ONLY, NOFORMAT, NOINIT, NAME = N'Test1-Full Database Backup', SKIP, NOREWIND, NOUNLOAD, STATS = 10
GO
--Fails... It is being used by database 'Test1'.
RESTORE DATABASE [Test2] FROM DISK = N'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\Backup\Test1.bak'
WITH REPLACE, NOUNLOAD, STATS = 5
GO
DROP DATABASE Test1
GO
--Now works, but the files are Test1.mdf and Test1.ldf
RESTORE DATABASE [Test2] FROM DISK = N'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\Backup\Test1.bak'
WITH REPLACE, NOUNLOAD, STATS = 5
GO
DROP DATABASE Test2
GO
CREATE DATABASE [Test2]
ON PRIMARY
( NAME = N'Test1', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\DATA\Test2.mdf' , SIZE = 8192KB , FILEGROWTH = 65536KB )
LOG ON
( NAME = N'Test1_log', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\DATA\Test2_log.ldf' , SIZE = 8192KB , FILEGROWTH = 65536KB )
GO
--Now the same command works, because Test1.mdf and Test1.ldf are not in use.
RESTORE DATABASE [Test2] FROM DISK = N'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\Backup\Test1.bak'
WITH REPLACE, NOUNLOAD, STATS = 5
GO
This is Microsoft's explanation of the REPLACE option:
https://learn.microsoft.com/en-us/sql/t-sql/statements/restore-statements-transact-sql?view=sql-server-2017#REPLACEoption
REPLACE Option Impact
REPLACE should be used rarely and only after careful consideration. Restore normally prevents accidentally overwriting a database with a different database. If the database specified in a RESTORE statement already exists on the current server and the specified database family GUID differs from the database family GUID recorded in the backup set, the database is not restored. This is an important safeguard.
The REPLACE option overrides several important safety checks that restore normally performs. The overridden checks are as follows:
Restoring over an existing database with a backup taken of another database.
With the REPLACE option, restore allows you to overwrite an existing database with whatever database is in the backup set, even if the specified database name differs from the database name recorded in the backup set. This can result in accidentally overwriting a database by a different database.
Restoring over a database using the full or bulk-logged recovery model where a tail-log backup has not been taken and the STOPAT option is not used.
With the REPLACE option, you can lose committed work, because the log written most recently has not been backed up.
Overwriting existing files.
For example, a mistake could allow overwriting files of the wrong type, such as .xls files, or that are being used by another database that is not online. Arbitrary data loss is possible if existing files are overwritten, although the restored database is complete.

The way I found is like following:
Bring your database offline -> tasks-> bring it offline
Go to your Database in the Windows Explorer and delete your mdf and log.ldf
C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\DATA\Test1.mdf
Let your Sql script run
That's it.
In general I think it is better to create a backup and restore this. In the database.
right click on database -> Tasks -> Take Offline
right click on database -> Tasks -> Restore -> Database
Select a Page -> General -> chose your file under device
Select a Page -> Options -> mark Overwrite the existing database (with replace)
Let it run

Related

SSIS Restore Database with Dynamic File Name

Every week I have to restore 2 different databases. I submit a ticket to Epi support and they give me .BAK files with different dates appended to them.
Running SQL Server 2014 (on remote server), Visual Studio 19 (on local machine), and Integration Service Project.
So far, my SSIS package removes previous .BAK files from SQL Server Backup directory, moves the new ones from my Downloads folder to the SQL Server Backup directory, and deletes one of previously existing backups (I like to delete them because WITH REPLACE does not update the LAST RESTORED DATE and I like to keep those up-to-date).
I would think that I could re-use the filename and the directory from when I move the BAK files with a File System Task in order to generate the RESTORE DATABASE statement, but I am not sure how to pass those fields in SSIS.
Now, I need to EXECUTE SQL TASK to RESTORE DATABASE, but it is hard to execute this in one statement with a dynamic filename.
I have looked at many different articles and tried different things, but none have worked.
The following script is what I want to be able to run, but I get errors even when the filename is hardcoded:
RESTORE DATABASE [Insite.Commerce.horizon.Sandbox] FROM DISK = 'C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\Backup\horizon-sandbox-20210412.bak'
The errors that this draws are below:
Msg 5133, Level 16, State 1, Line 2 Directory lookup for the file "d:\sqldata\\Insite.Commerce.horizon.mdf" failed with the operating system error 21(The device is not ready.).
Msg 3156, Level 16, State 3, Line 2 File 'Insite.Commerce.horizon' cannot be restored to 'd:\sqldata\\Insite.Commerce.horizon.mdf'. Use WITH MOVE to identify a valid location for the file.
Msg 5133, Level 16, State 1, Line 2 Directory lookup for the file "l:\sqllogs\\Insite.Commerce.horizon_log.ldf" failed with the operating system error 3(The system cannot find the path specified.).
Msg 3156, Level 16, State 3, Line 2 File 'Insite.Commerce.horizon_log' cannot be restored to 'l:\sqllogs\\Insite.Commerce.horizon_log.ldf'. Use WITH MOVE to identify a valid location for the file.
Msg 3119, Level 16, State 1, Line 2 Problems were identified while planning for the RESTORE statement. Previous messages provide details.
Msg 3013, Level 16, State 1, Line 2 RESTORE DATABASE is terminating abnormally.
I am not sure how to address these errors and/or if they are even relevant because I do not know if I am going about a database restore in SSIS the right way.
Regardless, as of now, I am trying to execute the following SQL statement in SSIS in order to return the SQL statement it generates as a result or variable that can be passed to another SQL statement and then executed. See below:
DECLARE #dirPath nvarchar(500) = 'C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\Backup\'
DECLARE #tblgetfileList TABLE
(FileName nvarchar(500)
,depth int
,isFile int)
INSERT INTO #tblgetfileList
EXEC xp_DirTree #dirPath,1,1
select 'RESTORE DATABASE [Insite.Commerce.horizon.Sandbox] FROM DISK = ''C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\Backup\' + FileName + '''' from #tblgetfileList where isFile = 1 and FileName like 'horizon-sandbox-%.bak'
The output of this is: RESTORE DATABASE [Insite.Commerce.horizon.Sandbox] FROM DISK = 'C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\Backup\horizon-sandbox-20210412.bak'
I tried storing this output as a variable and then executing it. See following script:
DECLARE #dirPath nvarchar(500) = 'C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\Backup\'
DECLARE #tblgetfileList TABLE
(FileName nvarchar(500)
,depth int
,isFile int)
INSERT INTO #tblgetfileList
EXEC xp_DirTree #dirPath,1,1
declare #sqltext varchar(max)
set #sqltext = '
select ''RESTORE DATABASE [Insite.Commerce.horizon.Sandbox] FROM DISK = ''C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\Backup\ + FileName + '''' from #tblgetfileList where isFile = 1 and FileName like ''horizon-sandbox-%.bak''
'
exec (#sqltext)
(The quotes of #sqltext might not be perfect, but I fixed them when trying to run it)
Also, any local file directory seen in the code here I have to prepend the IPv4 Address of the remote server and then change the file directory to "C$<insert rest of directory here>". So, it looks something like "\\C$<rest of directory>". This is because I am running the SSIS package on my local machine and it is doing this work on the remote server.
Long story short, I am stuck as to what to do. I do not know the best way of going about this nor do I know how to execute upon it.
I use TSQL all the time and would consider myself intermediate, but passing variables, recursive queries, and automation/programming are some things that I DEFINITELY need to work on. So, I could be making this way more complex than it needs to be and it could be a simple fix.
Is there an easier method? Can I utilize what I have already done? I would assume that I could just write one query to do this and then execute that in SSIS Execute SQL Task or Execute TSQL Statement Task.
The end goal is just to be able to grab the name of the new BAK file and restore that database in SQL Server
Let me know if you need more information! I can edit the question as necessary.
Any and all help is welcome!
Thank you in advance!
WITH MOVE needed to be used, but the DB name changed so all the logical filenames also had to change.
In short, what I did was use the UX in SSMS to set-up the database restore.
I picked my file from my device and changed the Restore To name as necessary. Then, I clicked "Script" in the top left of the Restore Database window and then clicked "New Query Editor Window".
This scripted everything out to a new query. It included the WITH MOVEs that needed to take place and a few other settings.
Although, how I got this to work in SSIS was I copied that new script to an Execute SQL Task in SSIS, but I added that text into another dynamic query that grabbed the filename I needed.
Here is the full query for the restore:
DECLARE #dirPath nvarchar(500) = '\\<IPv4 Address>\C$\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\Backup\'
DECLARE #tblgetfileList TABLE
(FileName nvarchar(500)
,depth int
,isFile int)
INSERT INTO #tblgetfileList
EXEC xp_DirTree #dirPath,1,1
declare #sqltext varchar(max)
select #sqltext=
'RESTORE DATABASE [Insite.Commerce.horizon.Sandbox] FROM DISK = N''\\<IPv4 Address>\C$\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\Backup\' + FileName + '''' +
' WITH FILE = 1, MOVE N''Insite.Commerce.horizon'' TO N''\\<IPv4 Address>\C$\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\DATA\Insite.Commerce.horizon.Sandbox.mdf'', ' +
'MOVE N''Insite.Commerce.horizon_log'' TO N''\\<IPv4 Address>\C$\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\DATA\Insite.Commerce.horizon.Sandbox_log.ldf'', NOUNLOAD, STATS = 5'
from #tblgetfileList where isFile = 1 and FileName like 'horizon-sandbox-%.bak'
exec (#sqltext)

partitioned Database Restore issue

I am having a issue for restoring a partitioned database using the below script.
I have 7 partitions in the database.
here is the code .
declare #Sql varchar(max)
SET #Sql = 'RESTORE DATABASE [NEWS]
FILE = ''DEMO'',
FILEGROUP = ''DEMO''
FROM DISK = ''C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\Backup\DEMO_clone.bak
WITH FILE = 1, NOUNLOAD, REPLACE, STATS = 10
GO
RESTORE LOG [NEWS]
FROM DISK = ''C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\Backup\DEMO_clone.bak''
WITH RECOVERY
GO'''
EXEC (#Sql)
Its showing the error as incorrect syntax near '\'.
Anyone knows what is the issue is ?
GO is not a T-SQL command so you can't include it in a dynamic SQL script. GO is a batch separator interpreted by SQL Server client tools.
There is no need for the GO batch separators or dynamic SQL in here so I'm guessing the SQL in your question is actually built dynamically. There are also some other errors like misplaced quotes in your code. Below is the corrected version. I think you may need to add NORECOVERY to the RESTORE DATABASE too:
SET #Sql = 'RESTORE DATABASE [NEWS]
FILE = ''DEMO'',
FILEGROUP = ''DEMO''
FROM DISK = ''C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\Backup\DEMO_clone.bak''
WITH FILE = 1, NOUNLOAD, REPLACE, STATS = 10;
RESTORE LOG [NEWS]
FROM DISK = ''C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\Backup\DEMO_clone.bak'';
';

How to attach a mdf file bypassing filestream information?

I have a mdf and ldf files from a database that in principle have filestream data too.
Since i need to debug an issue related only to the relational tables and the filestream folder is too big I wonder whether there is a way to attach a db only from mdf and ldf and somehow skipping filestream
This is a typical attach query taken from this question:
USE [master]
GO
CREATE DATABASE [AdventureWorks2008] ON
( FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL10.SQLTRAININGKIT\MSSQL\DATA\AdventureWorks2008_Data.mdf' ),
( FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL10.SQLTRAININGKIT\MSSQL\DATA\AdventureWorks2008_Log.ldf' ),
FILEGROUP [PRIMARY] CONTAINS FILESTREAM DEFAULT
( NAME = N'Documents', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL10.SQLTRAININGKIT\MSSQL\DATA\Documents' )
FOR ATTACH
GO
Since i do not have the filestream data how can i do?
As far as I know there is no way to do this with attaching (since attach requires all files to be present). Also look at this answer.
There is alternative though. If your database is in full recovery mode you can take partial backup without filestream group. You can then restore from that backup and any missing filegroup will be in offline mode and inaccessible. Any query that will try to use objects from missing filegroup will fail.
Sample commands:
BACKUP DATABASE [Demo]
FILEGROUP = N'PRIMARY'
TO DISK = N'.\MSSQL\Backup\Demo.bak'
WITH COPY_ONLY, NOFORMAT, NOINIT, NAME = N'Demo-Full Database Backup', SKIP, NOREWIND, NOUNLOAD, STATS = 10
GO
RESTORE DATABASE [OtherDatabase]
FILE = N'Demo' FROM DISK = N'.\MSSQL\Backup\Demo.bak' WITH FILE = 1,
MOVE N'Demo' TO N'.\MSSQL\DATA\OtherDatabase.mdf',
MOVE N'Demo_log' TO N'.\MSSQL\DATA\OtherDatabase_Log.ldf',
MOVE N'FStream' TO N'.\MSSQL\DATA\OtherDatabase_Documents', NOUNLOAD, STATS = 10
GO
EDIT:
Some additional helpful links regarding backing and restoring files/filegroups:
https://learn.microsoft.com/en-us/sql/relational-databases/backup-restore/back-up-files-and-filegroups-sql-server
https://learn.microsoft.com/en-us/sql/relational-databases/backup-restore/restore-files-and-filegroups-sql-server
EDIT 2:
If your database is in simple recovery mode and you want to take backup of only specific filegroups, you have to make all other filegroups readonly. Then you can use READ_WRITE_FILEGROUPS to backup only R/W filegroups.
Sample commands:
USE [master]
GO
ALTER DATABASE [Demo] MODIFY FILEGROUP [FilestreamGroup] READONLY
GO
BACKUP DATABASE [Demo] READ_WRITE_FILEGROUPS TO DISK = N'.\MSSQL\Backup\EFCoreDemo.bak'
WITH COPY_ONLY, NOFORMAT, NOINIT, NAME = N'Demo-Full Database Backup', SKIP, NOREWIND, NOUNLOAD, STATS = 10
GO
ALTER DATABASE [Demo] MODIFY FILEGROUP [FilestreamGroup] READ_WRITE
GO
RESTORE DATABASE [OtherDatabase]
FILE = N'Demo' FROM DISK = N'.\MSSQL\Backup\EFCoreDemo.bak' WITH FILE = 1,
MOVE N'Demo' TO N'.\MSSQL\DATA\OtherDatabase.mdf',
MOVE N'Demo_log' TO N'.\MSSQL\DATA\OtherDatabase_Log.ldf',
MOVE N'FStream' TO N'.\MSSQL\DATA\OtherDatabase_Documents', NOUNLOAD, STATS = 10
GO

How can I copy an entire database to another on the same server?

I want to copy a database A into database B (same SQL Server) entirely - what would be the easiest SQL or utility command to do that? (database B should be 100% erased before the copy)
Note that it can be non-SQL thing, but in any case I need to run it as one-click. So some utility that does that is also acceptable as far as I can run copying with one click or from command line.
SQL Server 2008R2 or 2012.
Best, Askar
I made a soultion based on David's thoughts. It looks like this (Prod - source DB, Demo - destination copy)
DECLARE #backup varchar(300) = 'e:\Prod.bak';
DECLARE #logic_db_file varchar(300) = 'koe';
DECLARE #logic_log_file varchar(300) = 'koe_log';
DECLARE #out_db_file varchar(300) = 'c:\Program Files\Microsoft SQL Server\MSSQL11.SQLEXPRESS\MSSQL\DATA\Demo.mdf';
DECLARE #out_log_file varchar(300) = 'c:\Program Files\Microsoft SQL Server\MSSQL11.SQLEXPRESS\MSSQL\DATA\Demo_log.ldf';
PRINT N'Backing up Production database.'
BACKUP DATABASE Prod
TO DISK = #backup WITH FORMAT;
PRINT N'Restoring FILELISTONLY, see log file for data.'
-- Restore the files to Demo
RESTORE FILELISTONLY
FROM DISK = #backup
PRINT N'Getting exclusive access to Demo'
Alter database Demo set single_user with rollback immediate
drop database Demo
PRINT N'Restoring backup to Test database'
RESTORE DATABASE Demo
FROM DISK = #backup
WITH REPLACE,
MOVE #logic_db_file TO #out_db_file,
MOVE #logic_log_file TO #out_log_file
GO
-- if you want you can do some SQL fixes to newly made DB
PRINT N'Changing normal users to demo users, if account type is OK'
USE Demo;
UPDATE USER_ACCOUNTS SET ACCOUNT_TYPE=3 WHERE (STATUS=1) AND (ACCOUNT_TYPE=1);
GO

how to restore a sql server backup containing filestream data assigning a new folder name for filestream data?

To create a new database and populate it from a backup I typically do:
CREATE DATABASE MyDatabase -- I create an empty database
-- I retrieve the filepath of mdf and ldf files
DECLARE #FileData varchar(1000)
DECLARE #FileLog varchar(1000)
set #FileData = (select filename from MyDatabase.dbo.sysfiles where fileid = 1)
set #FileLog = (select filename from MyDatabase.dbo.sysfiles where fileid = 2)
-- I Restore the database from backup by substituting the mdf and ldf files
RESTORE DATABASE MyDatabase
FROM DISK = 'c:\Test\Test.bak'
WITH REPLACE,
MOVE 'MyApp_Data' TO #FileData, -- I forced the name to be MyApp_Data for simplicity
MOVE 'MyApp_Log' TO #FileLog -- I forced the name to be MyApp_Log for simplicity
How to do the same for the filestream data too? May I force the folder creation for filestream data or should I create it manually?
May you also comment on my approach?
I think you can use the MOVE option with the filestream data as well. You can run the following command to view the file list in your backup file. This will help identify the filestream file.
RESTORE FILELISTONLY
FROM DISK = 'c:\Test\Test.bak'
As for your overall approach, I think you may be doing more than you need to do. You don't have to create a database before you restore a backup to it. The RESTORE command will take care of the create DB steps.
So, your RESTORE command would be the same as your using, except that you can exclude the REPLACE option. You can specify your destination file information in the MOVE option.

Resources