How to implement a for each loop in SQL - sql-server

How to implement a for each loop in SQL?
I am looking for a way to automate the backing up of databases for SQL Server 2019 to a given folder, with the following name syntax:
<Date><DB Name>
I am hoping to be able to a list of all databases in a temporary table:
DECLARE #DBList TABLE (DBName VARCHAR(40))
INSERT INTO #DBList
SELECT name
FROM master.dbo.sysdatabases
and then run each record agents function.
At the moment this is how I do it. It works is a vile violation of the write-once principle
-- Initialize variables
DECLARE #Date NVARCHAR(MAX) = CONVERT(VARCHAR, GETDATE(), 112);
DECLARE #DBName SYSNAME;
DECLARE #Path NVARCHAR(MAX);
USE [master];
-- BackUp Database
SET #DBName = 'DATABASE01';
SET #Path = 'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\Backup\' + #Date + #DBName +'.bak';
BACKUP DATABASE #DBName TO DISK = #Path;
SET #DBName = 'DATABASE02';
SET #Path = 'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\Backup\' + #Date + #DBName +'.bak';
BACKUP DATABASE #DBName TO DISK = #Path;
SET #DBName = 'DATABASE03';
SET #Path = 'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\Backup\' + #Date + #DBName +'.bak';
BACKUP DATABASE #DBName TO DISK = #Path;
SET #DBName = 'DATABASE04';
SET #Path = 'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\Backup\' + #Date + #DBName +'.bak';
BACKUP DATABASE #DBName TO DISK = #Path;
SET #DBName = 'DATABASE05';
SET #Path = 'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\Backup\' + #Date + #DBName +'.bak';
BACKUP DATABASE #DBName TO DISK = #Path;

You can make use of loop in this way.
You need to create some sort of ranking to go through each row, and then get the value based on current rank.
DECLARE #DBList TABLE (DBName varchar(40),rankvalue int)
insert into #DBList SELECT name, DENSE_RANK() over (order by name) rankval FROM master.dbo.sysdatabases
Declare #Date NVARCHAR(max) = convert(varchar, getdate(), 112);
DECLARE #DBName SYSNAME;
DECLARE #Path NVARCHAR(MAX);
Declare #current int = 1 , #maxvalue int =(select max(rankvalue) from #DBList)
while #current <= #maxvalue
begin
Set #DBName = (select DBName from #DBList where rankvalue = #current)
Set #Path = 'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\Backup\' + #Date + #DBName +'.bak';
BACKUP DATABASE #DBName TO DISK = #Path
set #current = #current + 1
end

Related

Is there a function to compress 'bak' file in sql script?

In SQL Server, I want to clear personal info and backup it
Backup original DB
Restore as another DB name
Clear personal info in another DB
Backup another DB
Delete rest files
Zip Backup DB
I finished 1~5. but couldn't find a way to do 6.
I Want to compress bak file to zip here.
For instance, below code can be used in Powershell script. Is there a way to use this .Net function in SQL script?
[System.IO.Compression.ZipFile]::CreateFromDirectory($CurrentPath, $DeployHistoryFilePath)
Below is my full script.
DECLARE #DBName NVARCHAR(MAX) = N'TestDB'
DECLARE #BackupPath NVARCHAR(MAX) = N'D:\Database\Backup'
EXEC ('master.dbo.xp_create_subdir N'''+ #BackupPath +'''')
DECLARE #BackupName NVARCHAR(MAX) = N'OnCube_' + REPLACE(REPLACE(REPLACE(CONVERT(NVARCHAR(MAX), GETDATE(), 120), N'-', N''), N':', N''), N' ', N'_')
DECLARE #DiskFile NVARCHAR(MAX) = #BackupPath + N'\' + #BackupName + N'.bak'
BACKUP DATABASE #DBName TO DISK = #DiskFile
DECLARE #SQL NVARCHAR(MAX) = 'SELECT TOP (1) #OriginalMdf = name FROM ' + #DBName + '.sys.database_files WHERE file_id = 1'
DECLARE #OriginalMdf NVARCHAR(MAX)
EXEC sp_executesql #SQL, N'#OriginalMdf NVARCHAR(MAX) OUT', #OriginalMdf out
SET #SQL = 'SELECT TOP (1) #OriginalLdf = name FROM ' + #DBName + '.sys.database_files WHERE file_id = 2'
DECLARE #OriginalLdf NVARCHAR(MAX)
EXEC sp_executesql #SQL, N'#OriginalLdf NVARCHAR(MAX) OUT', #OriginalLdf out
DECLARE #PartialMdf NVARCHAR(MAX) = #BackupPath + N'\' + #BackupName + N'.mdf'
DECLARE #PartialLdf NVARCHAR(MAX) = #BackupPath + N'\' + #BackupName + N'_0.ldf'
RESTORE FILELISTONLY FROM DISK = #DiskFile
RESTORE DATABASE #BackupName
FROM DISK = #DiskFile
WITH MOVE #OriginalMdf TO #PartialMdf,
MOVE #OriginalLdf TO #PartialLdf
EXEC (N'
USE [' + #BackupName + ']
UPDATE Person
SET
PatientNo = NULL
, PatientName = N''Cleared'' + CONVERT(NVARCHAR(MAX), RawID)
, RoomNo = NULL
, BedNo = NULL
, Birthday = NULL
, Sex = NULL
, Address = NULL
, AdmitDate = NULL
, AdmitNo = NULL
, Description = NULL
, DischargedDate = NULL
')
DECLARE #ClearedDiskFile NVARCHAR(MAX) = #BackupPath + N'\' + #BackupName + N'_PatientInfoCleared.bak'
BACKUP DATABASE #BackupName TO DISK = #ClearedDiskFile
EXEC('DROP DATABASE [' + #BackupName + ']')
EXEC ('xp_cmdshell ''del "' + #DiskFile + '"''')
-- I Want to compress bak file to zip here
-- For instance, below code can be used in Powershell script. Is there a way to use this .Net function in SQL script?
-- [System.IO.Compression.ZipFile]::CreateFromDirectory($CurrentPath, $DeployHistoryFilePath)
PRINT N'Success to make ' + #ClearedDiskFile + '. Patient informations are all cleared'
Is there a way to use this .Net function in SQL script?
Yes, you can use SQL CLR with C#
see samples Using 7-zip and SharpZipLib here:
also , you can create zip file from SQL without Powershell script:
Create zip file from SQL Server

How to Backup and delete SQL Server database if the database created date is more than 3 months

How to Backup and delete SQL Server database if the database created date is more than 3 months
Be careful using something like this, this script could get you in to trouble. Notice I am only selecting the #tsql parameter, I commented out the EXEC so you can see what would be executing first.
/* Create a cursor to iterate through the databases you want to backup and delete */
DECLARE #tsql nvarchar(max)
DECLARE #dbname varchar(500)
DECLARE MyCursor CURSOR STATIC FORWARD_ONLY
FOR
SELECT [name]
FROM sys.databases
WHERE create_date < DATEADD (M, -3, GETDATE())
AND [name] NOT IN ('master', 'model', 'msdb', 'tempdb')
OPEN MyCursor
WHILE (1=1)
BEGIN
DECLARE
#Date varchar(20) = GETDATE()
FETCH NEXT FROM MyCursor INTO #dbname
IF ##FETCH_STATUS <> 0 BREAK
SET #tsql = 'BACKUP DATABASE [' + #dbname + '] TO DISK = N''S:\Backups\' + #dbname + ' ' + #Date + '.bak'' WITH NOFORMAT, NOINIT, SKIP, NOREWIND, NOUNLOAD, COMPRESSION, STATS = 10'
SELECT #tsql;
-- EXEC sp_executesql #tsql
SET #tsql = 'ALTER DATABASE [' + #dbname + '] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; DROP DATABASE [' + #dbname + ']'
SELECT #tsql
-- EXEC sp_executesql #tsql
END
CLOSE MyCursor;
DEALLOCATE MyCursor;
GO

How to create daily backup with unique name in sql server

I want to make full database backup of my server's all databases with unique name daily. For that I have an idea to keep timestamp which will make database copy separate.
Suppose there is a database on server named ABCD then it should be backuped like:
ABCD_21_03_2013
ABCD_22_03_2013
How can I do this. I don't know much about these types of SQL Backup JOBS.
To create a daily backup with a name such as Filename_MM_DD_YYYY:
In SSMS, right-click the database you want to backup
Select Tasks | Back Up
In the dialog, select the type and location of the backup
At the top of this dialog, select the Script Action to Job option in the Script drop down menu
A New Job dialog is opened and the first step creates a database backup
Go to the fist step and click Edit
Existing code looks like:
BACKUP DATABASE [AdventureWorks2012] TO DISK = N'E:\Test\AdventureWorks.bak' WITH NOFORMAT, NOINIT, NAME = N'AdventureWorks2012-Full Database Backup', SKIP, NOREWIND, NOUNLOAD, STATS = 10
GO
Replace it to be
DECLARE #SQLStatement VARCHAR(2000)
SET #SQLStatement = 'E:\Test\AdventureWorks_' + CONVERT(nvarchar(30), GETDATE(), 110) +'.bak'
BACKUP DATABASE [AdventureWorks2012] TO DISK = #SQLStatement
6 Save the job
The database backups will be named:
AdventureWorks_07-29-2013
AdventureWorks_07-30-2013
AdventureWorks_07-31-2013
Now all you have to do is make the right schedule
Automatic backup of all databases on the server.
About Jobs:
http://msdn.microsoft.com/en-us/library/ms190268.aspx
Query:
SET NOCOUNT ON;
DECLARE
#FileName NVARCHAR(1024)
, #DBName NVARCHAR(256)
, #PathName NVARCHAR(256)
, #Message NVARCHAR(2048)
, #IsCompressed BIT
SELECT
#PathName = 'D:\BACKUP\'
, #IsCompressed = 1
DECLARE db CURSOR LOCAL READ_ONLY FAST_FORWARD FOR
SELECT
sd.name
, file_path = #PathName + FileDate + '_' + name + '.bak'
FROM sys.databases sd
CROSS JOIN (
SELECT FileDate = 'ABCD_' + REPLACE(CONVERT(VARCHAR(10), GETDATE(), 103), '/', '_')
) fd
WHERE sd.state_desc != 'OFFLINE'
AND sd.name NOT IN ('master', 'model', 'msdb', 'tempdb')
ORDER BY sd.name
OPEN db
FETCH NEXT FROM db INTO
#DBName
, #FileName
WHILE ##FETCH_STATUS = 0 BEGIN
DECLARE #SQL NVARCHAR(MAX)
SELECT #Message = REPLICATE('-', 80) + CHAR(13) + CONVERT(VARCHAR(20), GETDATE(), 120) + N': ' + #DBName
RAISERROR (#Message, 0, 1) WITH NOWAIT
SELECT #SQL =
'BACKUP DATABASE [' + #DBName + ']
TO DISK = N''' + #FileName + '''
WITH FORMAT, ' + CASE WHEN #IsCompressed = 1 THEN N'COMPRESSION, ' ELSE '' END + N'INIT, STATS = 15;'
EXEC sys.sp_executesql #SQL
FETCH NEXT FROM db INTO
#DBName
, #FileName
END
CLOSE db
DEALLOCATE db
Output:
BACKUP DATABASE [AdventureWorks2008R2]
TO DISK = N'D:\BACKUP\ABCD_24_05_2013_AdventureWorks2008R2.bak'
WITH FORMAT, COMPRESSION, INIT, STATS = 15;
BACKUP DATABASE [AdventureWorks2008R2_Live]
TO DISK = N'D:\BACKUP\ABCD_24_05_2013_AdventureWorks2008R2_Live.bak'
WITH FORMAT, COMPRESSION, INIT, STATS = 15;
BACKUP DATABASE [AdventureWorks2012]
TO DISK = N'D:\BACKUP\ABCD_24_05_2013_AdventureWorks2012.bak'
WITH FORMAT, COMPRESSION, INIT, STATS = 15;
Results:
2013-05-24 09:54:34: AdventureWorks2008R2
15 percent processed.
30 percent processed.
45 percent processed.
60 percent processed.
75 percent processed.
90 percent processed.
Processed 23416 pages for database 'AdventureWorks2008R2', file 'AdventureWorks2008R2_Data' on file 1.
Processed 1 pages for database 'AdventureWorks2008R2', file 'AdventureWorks2008R2_Log' on file 1.
BACKUP DATABASE successfully processed 23417 pages in 4.052 seconds (45.148 MB/sec).
.....
I think, best way to perform schedule back-up is to create Job. Add your back-up job and schedule that on particular date and time.
How to create job
Some third-party backup programs, for example: EMS SQL Backup, allow setting templates for backup file names. Timestamp, server instance name, database names and other info can be added to file name.
Thanks for the posts
I just want to share a small update I have made on the script to make log database backups and automatically skip all databases with recovery model = simple which does not allow log backups. Hope it helps...
And yes, Mr. Ravi is right the best approach is to create a job, I've created a SP and am running from a job.
CREATE PROCEDURE sp_logbackup
AS
SET NOCOUNT ON;
DECLARE
#FileName NVARCHAR(1024)
, #DBName NVARCHAR(256)
, #PathName NVARCHAR(256)
, #Message NVARCHAR(2048)
, #IsCompressed BIT
SELECT
#PathName = '\\myServer\...'
, #IsCompressed = 1
DECLARE db CURSOR LOCAL READ_ONLY FAST_FORWARD FOR
SELECT
sd.name
, file_path = #PathName + name + '_' + FileDate + '.trn'
FROM sys.databases sd
CROSS JOIN (
SELECT FileDate = REPLACE(REPLACE(REPLACE(CONVERT(varchar,GETDATE(), 20),'-','_'),':',''),' ','')
) fd
WHERE sd.state_desc != 'OFFLINE'
AND sd.recovery_model != 3
AND sd.name NOT IN ('master', 'model', 'msdb', 'tempdb')
ORDER BY sd.name
OPEN db
FETCH NEXT FROM db INTO
#DBName
, #FileName
WHILE ##FETCH_STATUS = 0 BEGIN
DECLARE #SQL NVARCHAR(MAX)
SELECT #Message = REPLICATE('-', 80) + CHAR(13) + CONVERT(VARCHAR(20), GETDATE(), 120) + N': ' + #DBName
RAISERROR (#Message, 0, 1) WITH NOWAIT
SELECT #SQL =
'BACKUP LOG [' + #DBName + ']
TO DISK = N''' + #FileName + '''
WITH FORMAT, NAME = N''' + #DBName + ''', SKIP, REWIND, NOUNLOAD, STATS = 10;'
EXEC sys.sp_executesql #SQL
FETCH NEXT FROM db INTO
#DBName
, #FileName
END
CLOSE db
DEALLOCATE db

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'

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