Backup a SQL Server database with current date - sql-server

I'm trying to backup databases using the current date time to create a unique filename.
I keep getting incorrect syntax on the '+' before #TimeDate
My Code:
ALTER PROCEDURE [dbo].[BackUpDatabase]
(#DatabaseName Varchar(256))
AS
Begin
DECLARE #TimeDate Varchar(256);
SET #TimeDate = CONVERT(Varchar(256),GETDATE(), 113);
BACKUP DATABASE #DatabaseName
TO DISK = 'C:\SQLEXPRESSBACKUP\DB'+ #TimeDate + '.bak'
WITH NOFORMAT, INIT, NAME= #DatabaseName, SKIP, NOREWIND, NOUNLOAD, STATS=10
END
And I am unable to figure what to search on, to know what I am doing wrong ?

First of all, look at what your #TimeDate value generates:
DECLARE #TimeDate Varchar(256);
SET #TimeDate = CONVERT(Varchar(256),GETDATE(), 113);
SELECT #TimeDate
Produces:
26 Sep 2017 11:49:07:650
This wouldn't be an acceptable file name when appended to your string. So you need to cleanse this to remove : characters.
SET #TimeDate = REPLACE(CONVERT(Varchar(256),GETDATE(), 126), ':','');
Secondly, it doesn't like you concatenating a string here so use a variable instead that you can prepare before:
TO DISK = 'C:\SQLEXPRESSBACKUP\DB'+ #TimeDate + '.bak'
Change to:
DECLARE #location VARCHAR(100) = 'C:\SQLEXPRESSBACKUP\DB' + #timedate + '.bak'
Full soution:
DECLARE #TimeDate Varchar(256);
SET #TimeDate = REPLACE(CONVERT(Varchar(256),GETDATE(), 126), ':','');
DECLARE #location VARCHAR(100) = 'C:\SQLEXPRESSBACKUP\DB' + #timedate + '.bak'
BACKUP DATABASE #DatabaseName
TO DISK = #location
WITH NOFORMAT, INIT, NAME= #DatabaseName, SKIP, NOREWIND, NOUNLOAD, STATS=10

You can declare a variable #ExecuteQuery and then assign this variable value to TO DISK.
Like this.
DECLARE #ExecuteQuery Varchar(1000);
SET #ExecuteQuery = 'C:\SQLEXPRESSBACKUP\DB' + CONVERT(Varchar(256),GETDATE(), 113) + '.bak'
BACKUP DATABASE #DatabaseName
TO DISK = #ExecuteQuery
WITH NOFORMAT, INIT, NAME= #DatabaseName, SKIP, NOREWIND, NOUNLOAD, STATS=10

You'll need to use Dynamic SQL to build the query rather than trying to add in the variables:
ALTER PROCEDURE [dbo].[BackUpDatabase](#DatabaseName VARCHAR(256))
AS
BEGIN
DECLARE #TimeDate VARCHAR(256);
SET #TimeDate = CONVERT(VARCHAR(256), GETDATE(), 113);
DECLARE #sql VARCHAR(MAX);
SET #sql = '
BACKUP DATABASE ['+#DatabaseName+']
TO DISK = ''C:\SQLEXPRESSBACKUP\DB'+#TimeDate+'.bak''
WITH NOFORMAT, INIT, NAME= '''+#DatabaseName+''', SKIP, NOREWIND, NOUNLOAD, STATS=10';
PRINT #sql;
--exec (#sql)
END;
The PRINT #SQL will show you what is going to be run, if you are happy with it then remove that and uncomment out exec (#sql)

Related

How do I use variables in SQL Server backup disk path?

The following SQL statement works fine:
DECLARE #database VARCHAR(30) = 'DEMO';
BACKUP LOG #database
TO DISK = N'C:\zbackups\DEMO.trn'
WITH NOFORMAT, NOINIT,
NAME = N'MyDatabase Log Backup', SKIP, NOREWIND, NOUNLOAD, STATS = 10;
DBCC SHRINKFILE ('DEMO_log', 500);
This however does not:
DECLARE #database VARCHAR(30) = 'DEMO';
BACKUP LOG #database
TO DISK = 'C:\zbackups\' + #database + '.trn'
WITH NOFORMAT, NOINIT,
NAME = 'MyDatabase Log Backup', SKIP, NOREWIND, NOUNLOAD, STATS = 10;
DBCC SHRINKFILE (#database + '_log', 500);
Both the TO DISK directive and the DBCC SHRINKFILE parameters throw an error when I attempt to concatenate.
How do I use a variable in these location? Would also like to append some datetime parameters to the file path but haven't started researching that yet.
The problem is simply that you can't pass an expression there. Instead of:
TO DISK = 'C:\zbackups\' + #database + '.trn'
You need:
DECLARE #fullpath nvarchar(1024);
SET #fullpath = 'C:\zbackups\' + #database + '.trn';
...
TO DISK = #fullpath
...
You can also parameterize the backup name and log file name in a similar way (instead of hard-coding NAME = 'MyDatabase Log Backup'). But to get the shrink operation to run in the right context (if you really, really, really, really need it, that is), and you can rely on every single database being configured in the same way, you're going to have to hard-code the database name somewhere (e.g. in a USE statement), or use dynamic SQL to set the context to the right database, e.g.
DECLARE #database sysname = N'DEMO';
DECLARE #fullpath nvarchar(1024) = N'C:\zbackups\' + #database + '.trn',
#backup_name nvarchar(500) = #database + N' log backup',
#log_name nvarchar(500) = #database _ N'_log';
DECLARE #sql nvarchar(max) = N'BACKUP LOG #db
TO DISK = #fullpath
WITH NOFORMAT, NOINIT, NAME = #backup_name,
SKIP, NOREWIND, NOUNLOAD, STATS = 10;
DBCC SHRINKFILE(#log, 500);';
DECLARE #context nvarchar(1024) = QUOTENAME(#database)
+ N'.sys.sp_executesql';
EXECUTE #context #sql, -- run #sql but in the context of #database
N'#db sysname, #log nvarchar(500),
#fullpath nvarchar(1024), #backup_name nvarchar(500)',
#database, #log_name, #fullpath, #backup_name;
According to https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql?view=sql-server-ver16 a variable can be used. I think an expression would be listed if allowed. The same goes for DBCC SHRINKFILE. Try
DECLARE #database VARCHAR(30) = 'DEMO';
DECLARE #target VARCHAR(260) = 'C:\zbackups\' + #database + '.trn';
DECLARE #logical_name VARCHAR(128) = #database + '_log';
BACKUP LOG #database
TO DISK = #target
WITH NOFORMAT, NOINIT,
NAME = 'MyDatabase Log Backup', SKIP, NOREWIND, NOUNLOAD, STATS = 10;
DBCC SHRINKFILE (#logical_name, 500);
If the logical file name does not always follow the dbname_log pattern, perhaps lookup the file id and use that instead of the logical name in SHRINKFILE. Be careful of database scope on the connection.
DECLARE #file_id int
SELECT #file_id = file_id
FROM sys.database_files
WHERE physical_name = #target

How to implement a for each loop in SQL

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

Create a procedure with Alter Database option

I have more than 30 dbs which are encrypted with TDE. Now I have to make a backup of each db without encryption.
Following step are needed:
- Set encryption off
- Do a full backup of that db
- Set encryption on
(Sry, but I am not so good at coding)
Here an example what I did so far:
use [Testt]
ALTER DATABASE [Testt]
SET ENCRYPTION OFF
Go
BACKUP DATABASE [Testt]
TO DISK = N'J:\Backup\Testt_full.bak ' WITH NOFORMAT,COPY_ONLY, NOINIT,
NAME = N'J:\Testt', SKIP, NOREWIND, NOUNLOAD, STATS = 10
GO
GO
use [Testt]
ALTER DATABASE [Testt]
SET ENCRYPTION ON
GO
Is there any easier way to do this for 30 dbs? I thought about a procedure or a cursor
Thank you Claudio Biselli for your help:
I adjusted you cursor part:
DECLARE #dbName nvarchar(MAX) =''
DECLARE #sql nvarchar(MAX) = ''
DECLARE #sql2 nvarchar(MAX) = ''
DECLARE #sql3 nvarchar(MAX) = ''
DECLARE Crs CURSOR LOCAL FOR
SELECT d.name
FROM sys.databases d
INNER JOIN
sys.dm_database_encryption_keys e ON d.database_id = e.database_id
where d.name not like 'tempdb'
OPEN Crs
FETCH NEXT FROM Crs into #dbName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql= 'use ' + #dbName +
' ALTER DATABASE ' + #dbName + ' SET ENCRYPTION OFF'
select #sql
SET #sql2='BACKUP DATABASE ' + #dbName +
' TO DISK = ''J:\Backup\' + #dbName + '_full.bak'' WITH NOFORMAT, COPY_ONLY, NOINIT, NAME = ''J:\'+ #dbName + ''', SKIP, NOREWIND, NOUNLOAD, STATS = 10'
select #sql2
SET #sql3= 'use ' + #dbName +
' ALTER DATABASE ' + #dbName + ' SET ENCRYPTION ON'
select #sql3
FETCH NEXT FROM Crs into #dbName
END
CLOSE Crs
DEALLOCATE Crs
And it works:)
Store in #tmp your database names
DECLARE #dbName nvarchar(MAX) =''
DECLARE #sql nvarchar(MAX) = ''
DECLARE Crs CURSOR LOCAL FOR
SELECT data
FROM #tmp
OPEN Crs
FETCH NEXT FROM Crs into #dbName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql= 'use' + #dbName +
'ALTER DATABASE' + #dbName +
'SET ENCRYPTION OFF'
EXEC(#sql)
DECLARE #dName nvarchar(MAX) = 'J:\\' + #dbName
DECLARE #dPath nvarchar(MAX) = 'J:\\Backup\\' + #dbName + '_full.bak'
SET #sql='BACKUP DATABASE ' + #dbName +
'TO DISK = '+ #dPath +' WITH
NOFORMAT,COPY_ONLY, NOINIT,
NAME = '+ #dName +', SKIP, NOREWIND, NOUNLOAD, STATS = 10'
EXEC(#sql)
SET #sql= 'use ' + #dbName +
'ALTER DATABASE ' + #dbName +
'SET ENCRYPTION ON'
EXEC(#sql)
FETCH NEXT FROM Crs into #dbName
END
CLOSE Crs
DEALLOCATE Crs

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

Resources